roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298     if (this.cls && this.cls.length) {
299         Roo.get(document.body).addClass(this.cls);
300     }
301 };
302
303 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
304       
305         autoCreate : {
306         cls: 'container'
307     },
308     onRender : function(ct, position)
309     {
310        /* Roo.log("Roo.bootstrap.Body - onRender");
311         if (this.cls && this.cls.length) {
312             Roo.get(document.body).addClass(this.cls);
313         }
314         // style??? xttr???
315         */
316     }
317     
318     
319  
320    
321 });
322
323  /*
324  * - LGPL
325  *
326  * button group
327  * 
328  */
329
330
331 /**
332  * @class Roo.bootstrap.ButtonGroup
333  * @extends Roo.bootstrap.Component
334  * Bootstrap ButtonGroup class
335  * @cfg {String} size lg | sm | xs (default empty normal)
336  * @cfg {String} align vertical | justified  (default none)
337  * @cfg {String} direction up | down (default down)
338  * @cfg {Boolean} toolbar false | true
339  * @cfg {Boolean} btn true | false
340  * 
341  * 
342  * @constructor
343  * Create a new Input
344  * @param {Object} config The config object
345  */
346
347 Roo.bootstrap.ButtonGroup = function(config){
348     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
349 };
350
351 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
352     
353     size: '',
354     align: '',
355     direction: '',
356     toolbar: false,
357     btn: true,
358
359     getAutoCreate : function(){
360         var cfg = {
361             cls: 'btn-group',
362             html : null
363         }
364         
365         cfg.html = this.html || cfg.html;
366         
367         if (this.toolbar) {
368             cfg = {
369                 cls: 'btn-toolbar',
370                 html: null
371             }
372             
373             return cfg;
374         }
375         
376         if (['vertical','justified'].indexOf(this.align)!==-1) {
377             cfg.cls = 'btn-group-' + this.align;
378             
379             if (this.align == 'justified') {
380                 console.log(this.items);
381             }
382         }
383         
384         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
385             cfg.cls += ' btn-group-' + this.size;
386         }
387         
388         if (this.direction == 'up') {
389             cfg.cls += ' dropup' ;
390         }
391         
392         return cfg;
393     }
394    
395 });
396
397  /*
398  * - LGPL
399  *
400  * button
401  * 
402  */
403
404 /**
405  * @class Roo.bootstrap.Button
406  * @extends Roo.bootstrap.Component
407  * Bootstrap Button class
408  * @cfg {String} html The button content
409  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
410  * @cfg {String} size empty | lg | sm | xs
411  * @cfg {String} tag empty | a | input | submit
412  * @cfg {String} href empty or href
413  * @cfg {Boolean} disabled false | true
414  * @cfg {Boolean} isClose false | true
415  * @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
416  * @cfg {String} badge text for badge
417  * @cfg {String} theme default (or empty) | glow
418  * @cfg {Boolean} inverse false | true
419  * @cfg {Boolean} toggle false | true
420  * @cfg {String} ontext text for on toggle state
421  * @cfg {String} offtext text for off toggle state
422  * @cfg {Boolean} defaulton true | false
423  * @cfg {Boolean} preventDefault (true | false) default true
424  * @cfg {Boolean} removeClass true | false remove the standard class..
425  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
426  * 
427  * @constructor
428  * Create a new button
429  * @param {Object} config The config object
430  */
431
432
433 Roo.bootstrap.Button = function(config){
434     Roo.bootstrap.Button.superclass.constructor.call(this, config);
435     this.addEvents({
436         // raw events
437         /**
438          * @event click
439          * When a butotn is pressed
440          * @param {Roo.EventObject} e
441          */
442         "click" : true,
443          /**
444          * @event toggle
445          * After the button has been toggles
446          * @param {Roo.EventObject} e
447          * @param {boolean} pressed (also available as button.pressed)
448          */
449         "toggle" : true
450     });
451 };
452
453 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
454     html: false,
455     active: false,
456     weight: '',
457     size: '',
458     tag: 'button',
459     href: '',
460     disabled: false,
461     isClose: false,
462     glyphicon: '',
463     badge: '',
464     theme: 'default',
465     inverse: false,
466     
467     toggle: false,
468     ontext: 'ON',
469     offtext: 'OFF',
470     defaulton: true,
471     preventDefault: true,
472     removeClass: false,
473     name: false,
474     target: false,
475     
476     
477     pressed : null,
478      
479     
480     getAutoCreate : function(){
481         
482         var cfg = {
483             tag : 'button',
484             cls : 'roo-button',
485             html: ''
486         };
487         
488         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
489             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
490             this.tag = 'button';
491         } else {
492             cfg.tag = this.tag;
493         }
494         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
495         
496         if (this.toggle == true) {
497             cfg={
498                 tag: 'div',
499                 cls: 'slider-frame roo-button',
500                 cn: [
501                     {
502                         tag: 'span',
503                         'data-on-text':'ON',
504                         'data-off-text':'OFF',
505                         cls: 'slider-button',
506                         html: this.offtext
507                     }
508                 ]
509             };
510             
511             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
512                 cfg.cls += ' '+this.weight;
513             }
514             
515             return cfg;
516         }
517         
518         if (this.isClose) {
519             cfg.cls += ' close';
520             
521             cfg["aria-hidden"] = true;
522             
523             cfg.html = "&times;";
524             
525             return cfg;
526         }
527         
528          
529         if (this.theme==='default') {
530             cfg.cls = 'btn roo-button';
531             
532             //if (this.parentType != 'Navbar') {
533             this.weight = this.weight.length ?  this.weight : 'default';
534             //}
535             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
536                 
537                 cfg.cls += ' btn-' + this.weight;
538             }
539         } else if (this.theme==='glow') {
540             
541             cfg.tag = 'a';
542             cfg.cls = 'btn-glow roo-button';
543             
544             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
545                 
546                 cfg.cls += ' ' + this.weight;
547             }
548         }
549    
550         
551         if (this.inverse) {
552             this.cls += ' inverse';
553         }
554         
555         
556         if (this.active) {
557             cfg.cls += ' active';
558         }
559         
560         if (this.disabled) {
561             cfg.disabled = 'disabled';
562         }
563         
564         if (this.items) {
565             Roo.log('changing to ul' );
566             cfg.tag = 'ul';
567             this.glyphicon = 'caret';
568         }
569         
570         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
571          
572         //gsRoo.log(this.parentType);
573         if (this.parentType === 'Navbar' && !this.parent().bar) {
574             Roo.log('changing to li?');
575             
576             cfg.tag = 'li';
577             
578             cfg.cls = '';
579             cfg.cn =  [{
580                 tag : 'a',
581                 cls : 'roo-button',
582                 html : this.html,
583                 href : this.href || '#'
584             }];
585             if (this.menu) {
586                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
587                 cfg.cls += ' dropdown';
588             }   
589             
590             delete cfg.html;
591             
592         }
593         
594        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
595         
596         if (this.glyphicon) {
597             cfg.html = ' ' + cfg.html;
598             
599             cfg.cn = [
600                 {
601                     tag: 'span',
602                     cls: 'glyphicon glyphicon-' + this.glyphicon
603                 }
604             ];
605         }
606         
607         if (this.badge) {
608             cfg.html += ' ';
609             
610             cfg.tag = 'a';
611             
612 //            cfg.cls='btn roo-button';
613             
614             cfg.href=this.href;
615             
616             var value = cfg.html;
617             
618             if(this.glyphicon){
619                 value = {
620                             tag: 'span',
621                             cls: 'glyphicon glyphicon-' + this.glyphicon,
622                             html: this.html
623                         };
624                 
625             }
626             
627             cfg.cn = [
628                 value,
629                 {
630                     tag: 'span',
631                     cls: 'badge',
632                     html: this.badge
633                 }
634             ];
635             
636             cfg.html='';
637         }
638         
639         if (this.menu) {
640             cfg.cls += ' dropdown';
641             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
642         }
643         
644         if (cfg.tag !== 'a' && this.href !== '') {
645             throw "Tag must be a to set href.";
646         } else if (this.href.length > 0) {
647             cfg.href = this.href;
648         }
649         
650         if(this.removeClass){
651             cfg.cls = '';
652         }
653         
654         if(this.target){
655             cfg.target = this.target;
656         }
657         
658         return cfg;
659     },
660     initEvents: function() {
661        // Roo.log('init events?');
662 //        Roo.log(this.el.dom);
663        if (this.el.hasClass('roo-button')) {
664             this.el.on('click', this.onClick, this);
665        } else {
666             this.el.select('.roo-button').on('click', this.onClick, this);
667        }
668        
669        if(this.removeClass){
670            this.el.on('click', this.onClick, this);
671        }
672        
673        this.el.enableDisplayMode();
674         
675     },
676     onClick : function(e)
677     {
678         if (this.disabled) {
679             return;
680         }
681         
682         Roo.log('button on click ');
683         if(this.preventDefault){
684             e.preventDefault();
685         }
686         if (this.pressed === true || this.pressed === false) {
687             this.pressed = !this.pressed;
688             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
689             this.fireEvent('toggle', this, e, this.pressed);
690         }
691         
692         
693         this.fireEvent('click', this, e);
694     },
695     
696     /**
697      * Enables this button
698      */
699     enable : function()
700     {
701         this.disabled = false;
702         this.el.removeClass('disabled');
703     },
704     
705     /**
706      * Disable this button
707      */
708     disable : function()
709     {
710         this.disabled = true;
711         this.el.addClass('disabled');
712     },
713      /**
714      * sets the active state on/off, 
715      * @param {Boolean} state (optional) Force a particular state
716      */
717     setActive : function(v) {
718         
719         this.el[v ? 'addClass' : 'removeClass']('active');
720     },
721      /**
722      * toggles the current active state 
723      */
724     toggleActive : function()
725     {
726        var active = this.el.hasClass('active');
727        this.setActive(!active);
728        
729         
730     },
731     setText : function(str)
732     {
733         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
734     },
735     hide: function() {
736        
737      
738         this.el.hide();   
739     },
740     show: function() {
741        
742         this.el.show();   
743     }
744     
745     
746 });
747
748  /*
749  * - LGPL
750  *
751  * column
752  * 
753  */
754
755 /**
756  * @class Roo.bootstrap.Column
757  * @extends Roo.bootstrap.Component
758  * Bootstrap Column class
759  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
760  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
761  * @cfg {Number} md colspan out of 12 for computer-sized screens
762  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
763  * @cfg {String} html content of column.
764  * 
765  * @constructor
766  * Create a new Column
767  * @param {Object} config The config object
768  */
769
770 Roo.bootstrap.Column = function(config){
771     Roo.bootstrap.Column.superclass.constructor.call(this, config);
772 };
773
774 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
775     
776     xs: null,
777     sm: null,
778     md: null,
779     lg: null,
780     html: '',
781     offset: 0,
782     
783     getAutoCreate : function(){
784         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
785         
786         cfg = {
787             tag: 'div',
788             cls: 'column'
789         };
790         
791         var settings=this;
792         ['xs','sm','md','lg'].map(function(size){
793             if (settings[size]) {
794                 cfg.cls += ' col-' + size + '-' + settings[size];
795             }
796         });
797         if (this.html.length) {
798             cfg.html = this.html;
799         }
800         
801         return cfg;
802     }
803    
804 });
805
806  
807
808  /*
809  * - LGPL
810  *
811  * page container.
812  * 
813  */
814
815
816 /**
817  * @class Roo.bootstrap.Container
818  * @extends Roo.bootstrap.Component
819  * Bootstrap Container class
820  * @cfg {Boolean} jumbotron is it a jumbotron element
821  * @cfg {String} html content of element
822  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
823  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
824  * @cfg {String} header content of header (for panel)
825  * @cfg {String} footer content of footer (for panel)
826  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
827  * @cfg {String} tag (header|aside|section) type of HTML tag.
828
829  *     
830  * @constructor
831  * Create a new Container
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Container = function(config){
836     Roo.bootstrap.Container.superclass.constructor.call(this, config);
837 };
838
839 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
840     
841     jumbotron : false,
842     well: '',
843     panel : '',
844     header: '',
845     footer : '',
846     sticky: '',
847     tag : false,
848   
849      
850     getChildContainer : function() {
851         
852         if(!this.el){
853             return false;
854         }
855         
856         if (this.panel.length) {
857             return this.el.select('.panel-body',true).first();
858         }
859         
860         return this.el;
861     },
862     
863     
864     getAutoCreate : function(){
865         
866         var cfg = {
867             tag : this.tag || 'div',
868             html : '',
869             cls : ''
870         };
871         if (this.jumbotron) {
872             cfg.cls = 'jumbotron';
873         }
874         // - this is applied by the parent..
875         //if (this.cls) {
876         //    cfg.cls = this.cls + '';
877         //}
878         
879         if (this.sticky.length) {
880             
881             var bd = Roo.get(document.body);
882             if (!bd.hasClass('bootstrap-sticky')) {
883                 bd.addClass('bootstrap-sticky');
884                 Roo.select('html',true).setStyle('height', '100%');
885             }
886              
887             cfg.cls += 'bootstrap-sticky-' + this.sticky;
888         }
889         
890         
891         if (this.well.length) {
892             switch (this.well) {
893                 case 'lg':
894                 case 'sm':
895                     cfg.cls +=' well well-' +this.well;
896                     break;
897                 default:
898                     cfg.cls +=' well';
899                     break;
900             }
901         }
902         
903         var body = cfg;
904         
905         if (this.panel.length) {
906             cfg.cls += ' panel panel-' + this.panel;
907             cfg.cn = [];
908             if (this.header.length) {
909                 cfg.cn.push({
910                     
911                     cls : 'panel-heading',
912                     cn : [{
913                         tag: 'h3',
914                         cls : 'panel-title',
915                         html : this.header
916                     }]
917                     
918                 });
919             }
920             body = false;
921             cfg.cn.push({
922                 cls : 'panel-body',
923                 html : this.html
924             });
925             
926             
927             if (this.footer.length) {
928                 cfg.cn.push({
929                     cls : 'panel-footer',
930                     html : this.footer
931                     
932                 });
933             }
934             
935         }
936         
937         if (body) {
938             body.html = this.html || cfg.html;
939         }
940         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
941             cfg.cls =  'container';
942         }
943         
944         return cfg;
945     }
946    
947 });
948
949  /*
950  * - LGPL
951  *
952  * image
953  * 
954  */
955
956
957 /**
958  * @class Roo.bootstrap.Img
959  * @extends Roo.bootstrap.Component
960  * Bootstrap Img class
961  * @cfg {Boolean} imgResponsive false | true
962  * @cfg {String} border rounded | circle | thumbnail
963  * @cfg {String} src image source
964  * @cfg {String} alt image alternative text
965  * @cfg {String} href a tag href
966  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
967  * 
968  * @constructor
969  * Create a new Input
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Img = function(config){
974     Roo.bootstrap.Img.superclass.constructor.call(this, config);
975     
976     this.addEvents({
977         // img events
978         /**
979          * @event click
980          * The img click event for the img.
981          * @param {Roo.EventObject} e
982          */
983         "click" : true
984     });
985 };
986
987 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
988     
989     imgResponsive: true,
990     border: '',
991     src: '',
992     href: false,
993     target: false,
994
995     getAutoCreate : function(){
996         
997         var cfg = {
998             tag: 'img',
999             cls: (this.imgResponsive) ? 'img-responsive' : '',
1000             html : null
1001         }
1002         
1003         cfg.html = this.html || cfg.html;
1004         
1005         cfg.src = this.src || cfg.src;
1006         
1007         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1008             cfg.cls += ' img-' + this.border;
1009         }
1010         
1011         if(this.alt){
1012             cfg.alt = this.alt;
1013         }
1014         
1015         if(this.href){
1016             var a = {
1017                 tag: 'a',
1018                 href: this.href,
1019                 cn: [
1020                     cfg
1021                 ]
1022             }
1023             
1024             if(this.target){
1025                 a.target = this.target;
1026             }
1027             
1028         }
1029         
1030         
1031         return (this.href) ? a : cfg;
1032     },
1033     
1034     initEvents: function() {
1035         
1036         if(!this.href){
1037             this.el.on('click', this.onClick, this);
1038         }
1039     },
1040     
1041     onClick : function(e)
1042     {
1043         Roo.log('img onclick');
1044         this.fireEvent('click', this, e);
1045     }
1046    
1047 });
1048
1049  /*
1050  * - LGPL
1051  *
1052  * image
1053  * 
1054  */
1055
1056
1057 /**
1058  * @class Roo.bootstrap.Link
1059  * @extends Roo.bootstrap.Component
1060  * Bootstrap Link Class
1061  * @cfg {String} alt image alternative text
1062  * @cfg {String} href a tag href
1063  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1064  * @cfg {String} html the content of the link.
1065
1066  * 
1067  * @constructor
1068  * Create a new Input
1069  * @param {Object} config The config object
1070  */
1071
1072 Roo.bootstrap.Link = function(config){
1073     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1074     
1075     this.addEvents({
1076         // img events
1077         /**
1078          * @event click
1079          * The img click event for the img.
1080          * @param {Roo.EventObject} e
1081          */
1082         "click" : true
1083     });
1084 };
1085
1086 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1087     
1088     href: false,
1089     target: false,
1090
1091     getAutoCreate : function(){
1092         
1093         var cfg = {
1094             tag: 'a',
1095             html : this.html || 'html-missing'
1096         }
1097         
1098         
1099         if(this.alt){
1100             cfg.alt = this.alt;
1101         }
1102         cfg.href = this.href || '#';
1103         if(this.target){
1104             cfg.target = this.target;
1105         }
1106         
1107         return cfg;
1108     },
1109     
1110     initEvents: function() {
1111         
1112         if(!this.href){
1113             this.el.on('click', this.onClick, this);
1114         }
1115     },
1116     
1117     onClick : function(e)
1118     {
1119         //Roo.log('img onclick');
1120         this.fireEvent('click', this, e);
1121     }
1122    
1123 });
1124
1125  /*
1126  * - LGPL
1127  *
1128  * header
1129  * 
1130  */
1131
1132 /**
1133  * @class Roo.bootstrap.Header
1134  * @extends Roo.bootstrap.Component
1135  * Bootstrap Header class
1136  * @cfg {String} html content of header
1137  * @cfg {Number} level (1|2|3|4|5|6) default 1
1138  * 
1139  * @constructor
1140  * Create a new Header
1141  * @param {Object} config The config object
1142  */
1143
1144
1145 Roo.bootstrap.Header  = function(config){
1146     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1147 };
1148
1149 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1150     
1151     //href : false,
1152     html : false,
1153     level : 1,
1154     
1155     
1156     
1157     getAutoCreate : function(){
1158         
1159         var cfg = {
1160             tag: 'h' + (1 *this.level),
1161             html: this.html || 'fill in html'
1162         } ;
1163         
1164         return cfg;
1165     }
1166    
1167 });
1168
1169  
1170
1171  /*
1172  * Based on:
1173  * Ext JS Library 1.1.1
1174  * Copyright(c) 2006-2007, Ext JS, LLC.
1175  *
1176  * Originally Released Under LGPL - original licence link has changed is not relivant.
1177  *
1178  * Fork - LGPL
1179  * <script type="text/javascript">
1180  */
1181  
1182 /**
1183  * @class Roo.bootstrap.MenuMgr
1184  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1185  * @singleton
1186  */
1187 Roo.bootstrap.MenuMgr = function(){
1188    var menus, active, groups = {}, attached = false, lastShow = new Date();
1189
1190    // private - called when first menu is created
1191    function init(){
1192        menus = {};
1193        active = new Roo.util.MixedCollection();
1194        Roo.get(document).addKeyListener(27, function(){
1195            if(active.length > 0){
1196                hideAll();
1197            }
1198        });
1199    }
1200
1201    // private
1202    function hideAll(){
1203        if(active && active.length > 0){
1204            var c = active.clone();
1205            c.each(function(m){
1206                m.hide();
1207            });
1208        }
1209    }
1210
1211    // private
1212    function onHide(m){
1213        active.remove(m);
1214        if(active.length < 1){
1215            Roo.get(document).un("mouseup", onMouseDown);
1216             
1217            attached = false;
1218        }
1219    }
1220
1221    // private
1222    function onShow(m){
1223        var last = active.last();
1224        lastShow = new Date();
1225        active.add(m);
1226        if(!attached){
1227           Roo.get(document).on("mouseup", onMouseDown);
1228            
1229            attached = true;
1230        }
1231        if(m.parentMenu){
1232           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1233           m.parentMenu.activeChild = m;
1234        }else if(last && last.isVisible()){
1235           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1236        }
1237    }
1238
1239    // private
1240    function onBeforeHide(m){
1241        if(m.activeChild){
1242            m.activeChild.hide();
1243        }
1244        if(m.autoHideTimer){
1245            clearTimeout(m.autoHideTimer);
1246            delete m.autoHideTimer;
1247        }
1248    }
1249
1250    // private
1251    function onBeforeShow(m){
1252        var pm = m.parentMenu;
1253        if(!pm && !m.allowOtherMenus){
1254            hideAll();
1255        }else if(pm && pm.activeChild && active != m){
1256            pm.activeChild.hide();
1257        }
1258    }
1259
1260    // private
1261    function onMouseDown(e){
1262         Roo.log("on MouseDown");
1263         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1264            hideAll();
1265         }
1266         
1267         
1268    }
1269
1270    // private
1271    function onBeforeCheck(mi, state){
1272        if(state){
1273            var g = groups[mi.group];
1274            for(var i = 0, l = g.length; i < l; i++){
1275                if(g[i] != mi){
1276                    g[i].setChecked(false);
1277                }
1278            }
1279        }
1280    }
1281
1282    return {
1283
1284        /**
1285         * Hides all menus that are currently visible
1286         */
1287        hideAll : function(){
1288             hideAll();  
1289        },
1290
1291        // private
1292        register : function(menu){
1293            if(!menus){
1294                init();
1295            }
1296            menus[menu.id] = menu;
1297            menu.on("beforehide", onBeforeHide);
1298            menu.on("hide", onHide);
1299            menu.on("beforeshow", onBeforeShow);
1300            menu.on("show", onShow);
1301            var g = menu.group;
1302            if(g && menu.events["checkchange"]){
1303                if(!groups[g]){
1304                    groups[g] = [];
1305                }
1306                groups[g].push(menu);
1307                menu.on("checkchange", onCheck);
1308            }
1309        },
1310
1311         /**
1312          * Returns a {@link Roo.menu.Menu} object
1313          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1314          * be used to generate and return a new Menu instance.
1315          */
1316        get : function(menu){
1317            if(typeof menu == "string"){ // menu id
1318                return menus[menu];
1319            }else if(menu.events){  // menu instance
1320                return menu;
1321            }
1322            /*else if(typeof menu.length == 'number'){ // array of menu items?
1323                return new Roo.bootstrap.Menu({items:menu});
1324            }else{ // otherwise, must be a config
1325                return new Roo.bootstrap.Menu(menu);
1326            }
1327            */
1328            return false;
1329        },
1330
1331        // private
1332        unregister : function(menu){
1333            delete menus[menu.id];
1334            menu.un("beforehide", onBeforeHide);
1335            menu.un("hide", onHide);
1336            menu.un("beforeshow", onBeforeShow);
1337            menu.un("show", onShow);
1338            var g = menu.group;
1339            if(g && menu.events["checkchange"]){
1340                groups[g].remove(menu);
1341                menu.un("checkchange", onCheck);
1342            }
1343        },
1344
1345        // private
1346        registerCheckable : function(menuItem){
1347            var g = menuItem.group;
1348            if(g){
1349                if(!groups[g]){
1350                    groups[g] = [];
1351                }
1352                groups[g].push(menuItem);
1353                menuItem.on("beforecheckchange", onBeforeCheck);
1354            }
1355        },
1356
1357        // private
1358        unregisterCheckable : function(menuItem){
1359            var g = menuItem.group;
1360            if(g){
1361                groups[g].remove(menuItem);
1362                menuItem.un("beforecheckchange", onBeforeCheck);
1363            }
1364        }
1365    };
1366 }();/*
1367  * - LGPL
1368  *
1369  * menu
1370  * 
1371  */
1372
1373 /**
1374  * @class Roo.bootstrap.Menu
1375  * @extends Roo.bootstrap.Component
1376  * Bootstrap Menu class - container for MenuItems
1377  * @cfg {String} type type of menu
1378  * 
1379  * @constructor
1380  * Create a new Menu
1381  * @param {Object} config The config object
1382  */
1383
1384
1385 Roo.bootstrap.Menu = function(config){
1386     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1387     if (this.registerMenu) {
1388         Roo.bootstrap.MenuMgr.register(this);
1389     }
1390     this.addEvents({
1391         /**
1392          * @event beforeshow
1393          * Fires before this menu is displayed
1394          * @param {Roo.menu.Menu} this
1395          */
1396         beforeshow : true,
1397         /**
1398          * @event beforehide
1399          * Fires before this menu is hidden
1400          * @param {Roo.menu.Menu} this
1401          */
1402         beforehide : true,
1403         /**
1404          * @event show
1405          * Fires after this menu is displayed
1406          * @param {Roo.menu.Menu} this
1407          */
1408         show : true,
1409         /**
1410          * @event hide
1411          * Fires after this menu is hidden
1412          * @param {Roo.menu.Menu} this
1413          */
1414         hide : true,
1415         /**
1416          * @event click
1417          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1418          * @param {Roo.menu.Menu} this
1419          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1420          * @param {Roo.EventObject} e
1421          */
1422         click : true,
1423         /**
1424          * @event mouseover
1425          * Fires when the mouse is hovering over this menu
1426          * @param {Roo.menu.Menu} this
1427          * @param {Roo.EventObject} e
1428          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1429          */
1430         mouseover : true,
1431         /**
1432          * @event mouseout
1433          * Fires when the mouse exits this menu
1434          * @param {Roo.menu.Menu} this
1435          * @param {Roo.EventObject} e
1436          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1437          */
1438         mouseout : true,
1439         /**
1440          * @event itemclick
1441          * Fires when a menu item contained in this menu is clicked
1442          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1443          * @param {Roo.EventObject} e
1444          */
1445         itemclick: true
1446     });
1447     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1448 };
1449
1450 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1451     
1452    /// html : false,
1453     //align : '',
1454     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1455     type: false,
1456     /**
1457      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1458      */
1459     registerMenu : true,
1460     
1461     menuItems :false, // stores the menu items..
1462     
1463     hidden:true,
1464     
1465     parentMenu : false,
1466     
1467     getChildContainer : function() {
1468         return this.el;  
1469     },
1470     
1471     getAutoCreate : function(){
1472          
1473         //if (['right'].indexOf(this.align)!==-1) {
1474         //    cfg.cn[1].cls += ' pull-right'
1475         //}
1476         var cfg = {
1477             tag : 'ul',
1478             cls : 'dropdown-menu' ,
1479             style : 'z-index:1000'
1480             
1481         }
1482         
1483         if (this.type === 'submenu') {
1484             cfg.cls = 'submenu active'
1485         }
1486         
1487         return cfg;
1488     },
1489     initEvents : function() {
1490         
1491        // Roo.log("ADD event");
1492        // Roo.log(this.triggerEl.dom);
1493         this.triggerEl.on('click', this.onTriggerPress, this);
1494         this.triggerEl.addClass('dropdown-toggle');
1495         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1496
1497         this.el.on("mouseover", this.onMouseOver, this);
1498         this.el.on("mouseout", this.onMouseOut, this);
1499         
1500         
1501     },
1502     findTargetItem : function(e){
1503         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1504         if(!t){
1505             return false;
1506         }
1507         //Roo.log(t);         Roo.log(t.id);
1508         if(t && t.id){
1509             //Roo.log(this.menuitems);
1510             return this.menuitems.get(t.id);
1511             
1512             //return this.items.get(t.menuItemId);
1513         }
1514         
1515         return false;
1516     },
1517     onClick : function(e){
1518         Roo.log("menu.onClick");
1519         var t = this.findTargetItem(e);
1520         if(!t){
1521             return;
1522         }
1523         Roo.log(e);
1524         /*
1525         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1526             if(t == this.activeItem && t.shouldDeactivate(e)){
1527                 this.activeItem.deactivate();
1528                 delete this.activeItem;
1529                 return;
1530             }
1531             if(t.canActivate){
1532                 this.setActiveItem(t, true);
1533             }
1534             return;
1535             
1536             
1537         }
1538         */
1539         Roo.log('pass click event');
1540         
1541         t.onClick(e);
1542         
1543         this.fireEvent("click", this, t, e);
1544         
1545         this.hide();
1546     },
1547      onMouseOver : function(e){
1548         var t  = this.findTargetItem(e);
1549         //Roo.log(t);
1550         //if(t){
1551         //    if(t.canActivate && !t.disabled){
1552         //        this.setActiveItem(t, true);
1553         //    }
1554         //}
1555         
1556         this.fireEvent("mouseover", this, e, t);
1557     },
1558     isVisible : function(){
1559         return !this.hidden;
1560     },
1561      onMouseOut : function(e){
1562         var t  = this.findTargetItem(e);
1563         
1564         //if(t ){
1565         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1566         //        this.activeItem.deactivate();
1567         //        delete this.activeItem;
1568         //    }
1569         //}
1570         this.fireEvent("mouseout", this, e, t);
1571     },
1572     
1573     
1574     /**
1575      * Displays this menu relative to another element
1576      * @param {String/HTMLElement/Roo.Element} element The element to align to
1577      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1578      * the element (defaults to this.defaultAlign)
1579      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1580      */
1581     show : function(el, pos, parentMenu){
1582         this.parentMenu = parentMenu;
1583         if(!this.el){
1584             this.render();
1585         }
1586         this.fireEvent("beforeshow", this);
1587         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1588     },
1589      /**
1590      * Displays this menu at a specific xy position
1591      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1592      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1593      */
1594     showAt : function(xy, parentMenu, /* private: */_e){
1595         this.parentMenu = parentMenu;
1596         if(!this.el){
1597             this.render();
1598         }
1599         if(_e !== false){
1600             this.fireEvent("beforeshow", this);
1601             
1602             //xy = this.el.adjustForConstraints(xy);
1603         }
1604         //this.el.setXY(xy);
1605         //this.el.show();
1606         this.hideMenuItems();
1607         this.hidden = false;
1608         this.triggerEl.addClass('open');
1609         this.focus();
1610         this.fireEvent("show", this);
1611     },
1612     
1613     focus : function(){
1614         return;
1615         if(!this.hidden){
1616             this.doFocus.defer(50, this);
1617         }
1618     },
1619
1620     doFocus : function(){
1621         if(!this.hidden){
1622             this.focusEl.focus();
1623         }
1624     },
1625
1626     /**
1627      * Hides this menu and optionally all parent menus
1628      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1629      */
1630     hide : function(deep){
1631         
1632         this.hideMenuItems();
1633         if(this.el && this.isVisible()){
1634             this.fireEvent("beforehide", this);
1635             if(this.activeItem){
1636                 this.activeItem.deactivate();
1637                 this.activeItem = null;
1638             }
1639             this.triggerEl.removeClass('open');;
1640             this.hidden = true;
1641             this.fireEvent("hide", this);
1642         }
1643         if(deep === true && this.parentMenu){
1644             this.parentMenu.hide(true);
1645         }
1646     },
1647     
1648     onTriggerPress  : function(e)
1649     {
1650         
1651         Roo.log('trigger press');
1652         //Roo.log(e.getTarget());
1653        // Roo.log(this.triggerEl.dom);
1654         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1655             return;
1656         }
1657         if (this.isVisible()) {
1658             Roo.log('hide');
1659             this.hide();
1660         } else {
1661             this.show(this.triggerEl, false, false);
1662         }
1663         
1664         
1665     },
1666     
1667          
1668        
1669     
1670     hideMenuItems : function()
1671     {
1672         //$(backdrop).remove()
1673         Roo.select('.open',true).each(function(aa) {
1674             
1675             aa.removeClass('open');
1676           //var parent = getParent($(this))
1677           //var relatedTarget = { relatedTarget: this }
1678           
1679            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1680           //if (e.isDefaultPrevented()) return
1681            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1682         })
1683     },
1684     addxtypeChild : function (tree, cntr) {
1685         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1686           
1687         this.menuitems.add(comp);
1688         return comp;
1689
1690     },
1691     getEl : function()
1692     {
1693         Roo.log(this.el);
1694         return this.el;
1695     }
1696 });
1697
1698  
1699  /*
1700  * - LGPL
1701  *
1702  * menu item
1703  * 
1704  */
1705
1706
1707 /**
1708  * @class Roo.bootstrap.MenuItem
1709  * @extends Roo.bootstrap.Component
1710  * Bootstrap MenuItem class
1711  * @cfg {String} html the menu label
1712  * @cfg {String} href the link
1713  * @cfg {Boolean} preventDefault (true | false) default true
1714  * 
1715  * 
1716  * @constructor
1717  * Create a new MenuItem
1718  * @param {Object} config The config object
1719  */
1720
1721
1722 Roo.bootstrap.MenuItem = function(config){
1723     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1724     this.addEvents({
1725         // raw events
1726         /**
1727          * @event click
1728          * The raw click event for the entire grid.
1729          * @param {Roo.EventObject} e
1730          */
1731         "click" : true
1732     });
1733 };
1734
1735 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1736     
1737     href : false,
1738     html : false,
1739     preventDefault: true,
1740     
1741     getAutoCreate : function(){
1742         var cfg= {
1743             tag: 'li',
1744         cls: 'dropdown-menu-item',
1745             cn: [
1746             {
1747                 tag : 'a',
1748                 href : '#',
1749                 html : 'Link'
1750             }
1751             ]
1752     };
1753         
1754         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1755         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1756         return cfg;
1757     },
1758     
1759     initEvents: function() {
1760         
1761         //this.el.select('a').on('click', this.onClick, this);
1762         
1763     },
1764     onClick : function(e)
1765     {
1766         Roo.log('item on click ');
1767         //if(this.preventDefault){
1768         //    e.preventDefault();
1769         //}
1770         //this.parent().hideMenuItems();
1771         
1772         this.fireEvent('click', this, e);
1773     },
1774     getEl : function()
1775     {
1776         return this.el;
1777     }
1778 });
1779
1780  
1781
1782  /*
1783  * - LGPL
1784  *
1785  * menu separator
1786  * 
1787  */
1788
1789
1790 /**
1791  * @class Roo.bootstrap.MenuSeparator
1792  * @extends Roo.bootstrap.Component
1793  * Bootstrap MenuSeparator class
1794  * 
1795  * @constructor
1796  * Create a new MenuItem
1797  * @param {Object} config The config object
1798  */
1799
1800
1801 Roo.bootstrap.MenuSeparator = function(config){
1802     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1803 };
1804
1805 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1806     
1807     getAutoCreate : function(){
1808         var cfg = {
1809             cls: 'divider',
1810             tag : 'li'
1811         };
1812         
1813         return cfg;
1814     }
1815    
1816 });
1817
1818  
1819
1820  
1821 /*
1822 <div class="modal fade">
1823   <div class="modal-dialog">
1824     <div class="modal-content">
1825       <div class="modal-header">
1826         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1827         <h4 class="modal-title">Modal title</h4>
1828       </div>
1829       <div class="modal-body">
1830         <p>One fine body&hellip;</p>
1831       </div>
1832       <div class="modal-footer">
1833         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1834         <button type="button" class="btn btn-primary">Save changes</button>
1835       </div>
1836     </div><!-- /.modal-content -->
1837   </div><!-- /.modal-dialog -->
1838 </div><!-- /.modal -->
1839 */
1840 /*
1841  * - LGPL
1842  *
1843  * page contgainer.
1844  * 
1845  */
1846
1847 /**
1848  * @class Roo.bootstrap.Modal
1849  * @extends Roo.bootstrap.Component
1850  * Bootstrap Modal class
1851  * @cfg {String} title Title of dialog
1852  * @cfg {Boolean} specificTitle (true|false) default false
1853  * @cfg {Array} buttons Array of buttons or standard button set..
1854  * @cfg {String} buttonPosition (left|right|center) default right
1855  * 
1856  * @constructor
1857  * Create a new Modal Dialog
1858  * @param {Object} config The config object
1859  */
1860
1861 Roo.bootstrap.Modal = function(config){
1862     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1863     this.addEvents({
1864         // raw events
1865         /**
1866          * @event btnclick
1867          * The raw btnclick event for the button
1868          * @param {Roo.EventObject} e
1869          */
1870         "btnclick" : true
1871     });
1872     this.buttons = this.buttons || [];
1873 };
1874
1875 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1876     
1877     title : 'test dialog',
1878    
1879     buttons : false,
1880     
1881     // set on load...
1882     body:  false,
1883     
1884     specificTitle: false,
1885     
1886     buttonPosition: 'right',
1887     
1888     onRender : function(ct, position)
1889     {
1890         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1891      
1892         if(!this.el){
1893             var cfg = Roo.apply({},  this.getAutoCreate());
1894             cfg.id = Roo.id();
1895             //if(!cfg.name){
1896             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1897             //}
1898             //if (!cfg.name.length) {
1899             //    delete cfg.name;
1900            // }
1901             if (this.cls) {
1902                 cfg.cls += ' ' + this.cls;
1903             }
1904             if (this.style) {
1905                 cfg.style = this.style;
1906             }
1907             this.el = Roo.get(document.body).createChild(cfg, position);
1908         }
1909         //var type = this.el.dom.type;
1910         
1911         if(this.tabIndex !== undefined){
1912             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1913         }
1914         
1915         
1916         
1917         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1918         this.maskEl.enableDisplayMode("block");
1919         this.maskEl.hide();
1920         //this.el.addClass("x-dlg-modal");
1921     
1922         if (this.buttons.length) {
1923             Roo.each(this.buttons, function(bb) {
1924                 b = Roo.apply({}, bb);
1925                 b.xns = b.xns || Roo.bootstrap;
1926                 b.xtype = b.xtype || 'Button';
1927                 if (typeof(b.listeners) == 'undefined') {
1928                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1929                 }
1930                 
1931                 var btn = Roo.factory(b);
1932                 
1933                 btn.onRender(this.el.select('.modal-footer div').first());
1934                 
1935             },this);
1936         }
1937         // render the children.
1938         var nitems = [];
1939         
1940         if(typeof(this.items) != 'undefined'){
1941             var items = this.items;
1942             delete this.items;
1943
1944             for(var i =0;i < items.length;i++) {
1945                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1946             }
1947         }
1948         
1949         this.items = nitems;
1950         
1951         this.body = this.el.select('.modal-body',true).first();
1952         this.close = this.el.select('.modal-header .close', true).first();
1953         this.footer = this.el.select('.modal-footer',true).first();
1954         this.initEvents();
1955         //this.el.addClass([this.fieldClass, this.cls]);
1956         
1957     },
1958     getAutoCreate : function(){
1959         
1960         
1961         var bdy = {
1962                 cls : 'modal-body',
1963                 html : this.html || ''
1964         };
1965         
1966         var title = {
1967             tag: 'h4',
1968             cls : 'modal-title',
1969             html : this.title
1970         };
1971         
1972         if(this.specificTitle){
1973             title = this.title;
1974         };
1975         
1976         return modal = {
1977             cls: "modal fade",
1978             style : 'display: none',
1979             cn : [
1980                 {
1981                     cls: "modal-dialog",
1982                     cn : [
1983                         {
1984                             cls : "modal-content",
1985                             cn : [
1986                                 {
1987                                     cls : 'modal-header',
1988                                     cn : [
1989                                         {
1990                                             tag: 'button',
1991                                             cls : 'close',
1992                                             html : '&times'
1993                                         },
1994                                         title
1995                                     ]
1996                                 },
1997                                 bdy,
1998                                 {
1999                                     cls : 'modal-footer',
2000                                     cn : [
2001                                         {
2002                                             tag: 'div',
2003                                             cls: 'btn-' + this.buttonPosition
2004                                         }
2005                                     ]
2006                                     
2007                                 }
2008                                 
2009                                 
2010                             ]
2011                             
2012                         }
2013                     ]
2014                         
2015                 }
2016             ]
2017             
2018             
2019         };
2020           
2021     },
2022     getChildContainer : function() {
2023          
2024          return this.el.select('.modal-body',true).first();
2025         
2026     },
2027     getButtonContainer : function() {
2028          return this.el.select('.modal-footer div',true).first();
2029         
2030     },
2031     initEvents : function()
2032     {
2033         this.el.select('.modal-header .close').on('click', this.hide, this);
2034 //        
2035 //        this.addxtype(this);
2036     },
2037     show : function() {
2038         
2039         if (!this.rendered) {
2040             this.render();
2041         }
2042        
2043         this.el.addClass('on');
2044         this.el.removeClass('fade');
2045         this.el.setStyle('display', 'block');
2046         Roo.get(document.body).addClass("x-body-masked");
2047         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2048         this.maskEl.show();
2049         this.el.setStyle('zIndex', '10001');
2050         this.fireEvent('show', this);
2051         
2052         
2053     },
2054     hide : function()
2055     {
2056         Roo.log('Modal hide?!');
2057         this.maskEl.hide();
2058         Roo.get(document.body).removeClass("x-body-masked");
2059         this.el.removeClass('on');
2060         this.el.addClass('fade');
2061         this.el.setStyle('display', 'none');
2062         this.fireEvent('hide', this);
2063     },
2064     
2065     addButton : function(str, cb)
2066     {
2067          
2068         
2069         var b = Roo.apply({}, { html : str } );
2070         b.xns = b.xns || Roo.bootstrap;
2071         b.xtype = b.xtype || 'Button';
2072         if (typeof(b.listeners) == 'undefined') {
2073             b.listeners = { click : cb.createDelegate(this)  };
2074         }
2075         
2076         var btn = Roo.factory(b);
2077            
2078         btn.onRender(this.el.select('.modal-footer div').first());
2079         
2080         return btn;   
2081        
2082     },
2083     
2084     setDefaultButton : function(btn)
2085     {
2086         //this.el.select('.modal-footer').()
2087     },
2088     resizeTo: function(w,h)
2089     {
2090         // skip..
2091     },
2092     setContentSize  : function(w, h)
2093     {
2094         
2095     },
2096     onButtonClick: function(btn,e)
2097     {
2098         //Roo.log([a,b,c]);
2099         this.fireEvent('btnclick', btn.name, e);
2100     },
2101     setTitle: function(str) {
2102         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2103         
2104     }
2105 });
2106
2107
2108 Roo.apply(Roo.bootstrap.Modal,  {
2109     /**
2110          * Button config that displays a single OK button
2111          * @type Object
2112          */
2113         OK :  [{
2114             name : 'ok',
2115             weight : 'primary',
2116             html : 'OK'
2117         }], 
2118         /**
2119          * Button config that displays Yes and No buttons
2120          * @type Object
2121          */
2122         YESNO : [
2123             {
2124                 name  : 'no',
2125                 html : 'No'
2126             },
2127             {
2128                 name  :'yes',
2129                 weight : 'primary',
2130                 html : 'Yes'
2131             }
2132         ],
2133         
2134         /**
2135          * Button config that displays OK and Cancel buttons
2136          * @type Object
2137          */
2138         OKCANCEL : [
2139             {
2140                name : 'cancel',
2141                 html : 'Cancel'
2142             },
2143             {
2144                 name : 'ok',
2145                 weight : 'primary',
2146                 html : 'OK'
2147             }
2148         ],
2149         /**
2150          * Button config that displays Yes, No and Cancel buttons
2151          * @type Object
2152          */
2153         YESNOCANCEL : [
2154             {
2155                 name : 'yes',
2156                 weight : 'primary',
2157                 html : 'Yes'
2158             },
2159             {
2160                 name : 'no',
2161                 html : 'No'
2162             },
2163             {
2164                 name : 'cancel',
2165                 html : 'Cancel'
2166             }
2167         ]
2168 });
2169  /*
2170  * - LGPL
2171  *
2172  * messagebox - can be used as a replace
2173  * 
2174  */
2175 /**
2176  * @class Roo.MessageBox
2177  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2178  * Example usage:
2179  *<pre><code>
2180 // Basic alert:
2181 Roo.Msg.alert('Status', 'Changes saved successfully.');
2182
2183 // Prompt for user data:
2184 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2185     if (btn == 'ok'){
2186         // process text value...
2187     }
2188 });
2189
2190 // Show a dialog using config options:
2191 Roo.Msg.show({
2192    title:'Save Changes?',
2193    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2194    buttons: Roo.Msg.YESNOCANCEL,
2195    fn: processResult,
2196    animEl: 'elId'
2197 });
2198 </code></pre>
2199  * @singleton
2200  */
2201 Roo.bootstrap.MessageBox = function(){
2202     var dlg, opt, mask, waitTimer;
2203     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2204     var buttons, activeTextEl, bwidth;
2205
2206     
2207     // private
2208     var handleButton = function(button){
2209         dlg.hide();
2210         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2211     };
2212
2213     // private
2214     var handleHide = function(){
2215         if(opt && opt.cls){
2216             dlg.el.removeClass(opt.cls);
2217         }
2218         //if(waitTimer){
2219         //    Roo.TaskMgr.stop(waitTimer);
2220         //    waitTimer = null;
2221         //}
2222     };
2223
2224     // private
2225     var updateButtons = function(b){
2226         var width = 0;
2227         if(!b){
2228             buttons["ok"].hide();
2229             buttons["cancel"].hide();
2230             buttons["yes"].hide();
2231             buttons["no"].hide();
2232             //dlg.footer.dom.style.display = 'none';
2233             return width;
2234         }
2235         dlg.footer.dom.style.display = '';
2236         for(var k in buttons){
2237             if(typeof buttons[k] != "function"){
2238                 if(b[k]){
2239                     buttons[k].show();
2240                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2241                     width += buttons[k].el.getWidth()+15;
2242                 }else{
2243                     buttons[k].hide();
2244                 }
2245             }
2246         }
2247         return width;
2248     };
2249
2250     // private
2251     var handleEsc = function(d, k, e){
2252         if(opt && opt.closable !== false){
2253             dlg.hide();
2254         }
2255         if(e){
2256             e.stopEvent();
2257         }
2258     };
2259
2260     return {
2261         /**
2262          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2263          * @return {Roo.BasicDialog} The BasicDialog element
2264          */
2265         getDialog : function(){
2266            if(!dlg){
2267                 dlg = new Roo.bootstrap.Modal( {
2268                     //draggable: true,
2269                     //resizable:false,
2270                     //constraintoviewport:false,
2271                     //fixedcenter:true,
2272                     //collapsible : false,
2273                     //shim:true,
2274                     //modal: true,
2275                   //  width:400,
2276                   //  height:100,
2277                     //buttonAlign:"center",
2278                     closeClick : function(){
2279                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2280                             handleButton("no");
2281                         }else{
2282                             handleButton("cancel");
2283                         }
2284                     }
2285                 });
2286                 dlg.render();
2287                 dlg.on("hide", handleHide);
2288                 mask = dlg.mask;
2289                 //dlg.addKeyListener(27, handleEsc);
2290                 buttons = {};
2291                 this.buttons = buttons;
2292                 var bt = this.buttonText;
2293                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2294                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2295                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2296                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2297                 Roo.log(buttons)
2298                 bodyEl = dlg.body.createChild({
2299
2300                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2301                         '<textarea class="roo-mb-textarea"></textarea>' +
2302                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2303                 });
2304                 msgEl = bodyEl.dom.firstChild;
2305                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2306                 textboxEl.enableDisplayMode();
2307                 textboxEl.addKeyListener([10,13], function(){
2308                     if(dlg.isVisible() && opt && opt.buttons){
2309                         if(opt.buttons.ok){
2310                             handleButton("ok");
2311                         }else if(opt.buttons.yes){
2312                             handleButton("yes");
2313                         }
2314                     }
2315                 });
2316                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2317                 textareaEl.enableDisplayMode();
2318                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2319                 progressEl.enableDisplayMode();
2320                 var pf = progressEl.dom.firstChild;
2321                 if (pf) {
2322                     pp = Roo.get(pf.firstChild);
2323                     pp.setHeight(pf.offsetHeight);
2324                 }
2325                 
2326             }
2327             return dlg;
2328         },
2329
2330         /**
2331          * Updates the message box body text
2332          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2333          * the XHTML-compliant non-breaking space character '&amp;#160;')
2334          * @return {Roo.MessageBox} This message box
2335          */
2336         updateText : function(text){
2337             if(!dlg.isVisible() && !opt.width){
2338                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2339             }
2340             msgEl.innerHTML = text || '&#160;';
2341       
2342             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2343             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2344             var w = Math.max(
2345                     Math.min(opt.width || cw , this.maxWidth), 
2346                     Math.max(opt.minWidth || this.minWidth, bwidth)
2347             );
2348             if(opt.prompt){
2349                 activeTextEl.setWidth(w);
2350             }
2351             if(dlg.isVisible()){
2352                 dlg.fixedcenter = false;
2353             }
2354             // to big, make it scroll. = But as usual stupid IE does not support
2355             // !important..
2356             
2357             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2358                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2359                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2360             } else {
2361                 bodyEl.dom.style.height = '';
2362                 bodyEl.dom.style.overflowY = '';
2363             }
2364             if (cw > w) {
2365                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2366             } else {
2367                 bodyEl.dom.style.overflowX = '';
2368             }
2369             
2370             dlg.setContentSize(w, bodyEl.getHeight());
2371             if(dlg.isVisible()){
2372                 dlg.fixedcenter = true;
2373             }
2374             return this;
2375         },
2376
2377         /**
2378          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2379          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2380          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2381          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2382          * @return {Roo.MessageBox} This message box
2383          */
2384         updateProgress : function(value, text){
2385             if(text){
2386                 this.updateText(text);
2387             }
2388             if (pp) { // weird bug on my firefox - for some reason this is not defined
2389                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2390             }
2391             return this;
2392         },        
2393
2394         /**
2395          * Returns true if the message box is currently displayed
2396          * @return {Boolean} True if the message box is visible, else false
2397          */
2398         isVisible : function(){
2399             return dlg && dlg.isVisible();  
2400         },
2401
2402         /**
2403          * Hides the message box if it is displayed
2404          */
2405         hide : function(){
2406             if(this.isVisible()){
2407                 dlg.hide();
2408             }  
2409         },
2410
2411         /**
2412          * Displays a new message box, or reinitializes an existing message box, based on the config options
2413          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2414          * The following config object properties are supported:
2415          * <pre>
2416 Property    Type             Description
2417 ----------  ---------------  ------------------------------------------------------------------------------------
2418 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2419                                    closes (defaults to undefined)
2420 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2421                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2422 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2423                                    progress and wait dialogs will ignore this property and always hide the
2424                                    close button as they can only be closed programmatically.
2425 cls               String           A custom CSS class to apply to the message box element
2426 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2427                                    displayed (defaults to 75)
2428 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2429                                    function will be btn (the name of the button that was clicked, if applicable,
2430                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2431                                    Progress and wait dialogs will ignore this option since they do not respond to
2432                                    user actions and can only be closed programmatically, so any required function
2433                                    should be called by the same code after it closes the dialog.
2434 icon              String           A CSS class that provides a background image to be used as an icon for
2435                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2436 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2437 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2438 modal             Boolean          False to allow user interaction with the page while the message box is
2439                                    displayed (defaults to true)
2440 msg               String           A string that will replace the existing message box body text (defaults
2441                                    to the XHTML-compliant non-breaking space character '&#160;')
2442 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2443 progress          Boolean          True to display a progress bar (defaults to false)
2444 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2445 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2446 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2447 title             String           The title text
2448 value             String           The string value to set into the active textbox element if displayed
2449 wait              Boolean          True to display a progress bar (defaults to false)
2450 width             Number           The width of the dialog in pixels
2451 </pre>
2452          *
2453          * Example usage:
2454          * <pre><code>
2455 Roo.Msg.show({
2456    title: 'Address',
2457    msg: 'Please enter your address:',
2458    width: 300,
2459    buttons: Roo.MessageBox.OKCANCEL,
2460    multiline: true,
2461    fn: saveAddress,
2462    animEl: 'addAddressBtn'
2463 });
2464 </code></pre>
2465          * @param {Object} config Configuration options
2466          * @return {Roo.MessageBox} This message box
2467          */
2468         show : function(options)
2469         {
2470             
2471             // this causes nightmares if you show one dialog after another
2472             // especially on callbacks..
2473              
2474             if(this.isVisible()){
2475                 
2476                 this.hide();
2477                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2478                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2479                 Roo.log("New Dialog Message:" +  options.msg )
2480                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2481                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2482                 
2483             }
2484             var d = this.getDialog();
2485             opt = options;
2486             d.setTitle(opt.title || "&#160;");
2487             d.close.setDisplayed(opt.closable !== false);
2488             activeTextEl = textboxEl;
2489             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2490             if(opt.prompt){
2491                 if(opt.multiline){
2492                     textboxEl.hide();
2493                     textareaEl.show();
2494                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2495                         opt.multiline : this.defaultTextHeight);
2496                     activeTextEl = textareaEl;
2497                 }else{
2498                     textboxEl.show();
2499                     textareaEl.hide();
2500                 }
2501             }else{
2502                 textboxEl.hide();
2503                 textareaEl.hide();
2504             }
2505             progressEl.setDisplayed(opt.progress === true);
2506             this.updateProgress(0);
2507             activeTextEl.dom.value = opt.value || "";
2508             if(opt.prompt){
2509                 dlg.setDefaultButton(activeTextEl);
2510             }else{
2511                 var bs = opt.buttons;
2512                 var db = null;
2513                 if(bs && bs.ok){
2514                     db = buttons["ok"];
2515                 }else if(bs && bs.yes){
2516                     db = buttons["yes"];
2517                 }
2518                 dlg.setDefaultButton(db);
2519             }
2520             bwidth = updateButtons(opt.buttons);
2521             this.updateText(opt.msg);
2522             if(opt.cls){
2523                 d.el.addClass(opt.cls);
2524             }
2525             d.proxyDrag = opt.proxyDrag === true;
2526             d.modal = opt.modal !== false;
2527             d.mask = opt.modal !== false ? mask : false;
2528             if(!d.isVisible()){
2529                 // force it to the end of the z-index stack so it gets a cursor in FF
2530                 document.body.appendChild(dlg.el.dom);
2531                 d.animateTarget = null;
2532                 d.show(options.animEl);
2533             }
2534             return this;
2535         },
2536
2537         /**
2538          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2539          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2540          * and closing the message box when the process is complete.
2541          * @param {String} title The title bar text
2542          * @param {String} msg The message box body text
2543          * @return {Roo.MessageBox} This message box
2544          */
2545         progress : function(title, msg){
2546             this.show({
2547                 title : title,
2548                 msg : msg,
2549                 buttons: false,
2550                 progress:true,
2551                 closable:false,
2552                 minWidth: this.minProgressWidth,
2553                 modal : true
2554             });
2555             return this;
2556         },
2557
2558         /**
2559          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2560          * If a callback function is passed it will be called after the user clicks the button, and the
2561          * id of the button that was clicked will be passed as the only parameter to the callback
2562          * (could also be the top-right close button).
2563          * @param {String} title The title bar text
2564          * @param {String} msg The message box body text
2565          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2566          * @param {Object} scope (optional) The scope of the callback function
2567          * @return {Roo.MessageBox} This message box
2568          */
2569         alert : function(title, msg, fn, scope){
2570             this.show({
2571                 title : title,
2572                 msg : msg,
2573                 buttons: this.OK,
2574                 fn: fn,
2575                 scope : scope,
2576                 modal : true
2577             });
2578             return this;
2579         },
2580
2581         /**
2582          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2583          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2584          * You are responsible for closing the message box when the process is complete.
2585          * @param {String} msg The message box body text
2586          * @param {String} title (optional) The title bar text
2587          * @return {Roo.MessageBox} This message box
2588          */
2589         wait : function(msg, title){
2590             this.show({
2591                 title : title,
2592                 msg : msg,
2593                 buttons: false,
2594                 closable:false,
2595                 progress:true,
2596                 modal:true,
2597                 width:300,
2598                 wait:true
2599             });
2600             waitTimer = Roo.TaskMgr.start({
2601                 run: function(i){
2602                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2603                 },
2604                 interval: 1000
2605             });
2606             return this;
2607         },
2608
2609         /**
2610          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2611          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2612          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2613          * @param {String} title The title bar text
2614          * @param {String} msg The message box body text
2615          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2616          * @param {Object} scope (optional) The scope of the callback function
2617          * @return {Roo.MessageBox} This message box
2618          */
2619         confirm : function(title, msg, fn, scope){
2620             this.show({
2621                 title : title,
2622                 msg : msg,
2623                 buttons: this.YESNO,
2624                 fn: fn,
2625                 scope : scope,
2626                 modal : true
2627             });
2628             return this;
2629         },
2630
2631         /**
2632          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2633          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2634          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2635          * (could also be the top-right close button) and the text that was entered will be passed as the two
2636          * parameters to the callback.
2637          * @param {String} title The title bar text
2638          * @param {String} msg The message box body text
2639          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2640          * @param {Object} scope (optional) The scope of the callback function
2641          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2642          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2643          * @return {Roo.MessageBox} This message box
2644          */
2645         prompt : function(title, msg, fn, scope, multiline){
2646             this.show({
2647                 title : title,
2648                 msg : msg,
2649                 buttons: this.OKCANCEL,
2650                 fn: fn,
2651                 minWidth:250,
2652                 scope : scope,
2653                 prompt:true,
2654                 multiline: multiline,
2655                 modal : true
2656             });
2657             return this;
2658         },
2659
2660         /**
2661          * Button config that displays a single OK button
2662          * @type Object
2663          */
2664         OK : {ok:true},
2665         /**
2666          * Button config that displays Yes and No buttons
2667          * @type Object
2668          */
2669         YESNO : {yes:true, no:true},
2670         /**
2671          * Button config that displays OK and Cancel buttons
2672          * @type Object
2673          */
2674         OKCANCEL : {ok:true, cancel:true},
2675         /**
2676          * Button config that displays Yes, No and Cancel buttons
2677          * @type Object
2678          */
2679         YESNOCANCEL : {yes:true, no:true, cancel:true},
2680
2681         /**
2682          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2683          * @type Number
2684          */
2685         defaultTextHeight : 75,
2686         /**
2687          * The maximum width in pixels of the message box (defaults to 600)
2688          * @type Number
2689          */
2690         maxWidth : 600,
2691         /**
2692          * The minimum width in pixels of the message box (defaults to 100)
2693          * @type Number
2694          */
2695         minWidth : 100,
2696         /**
2697          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2698          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2699          * @type Number
2700          */
2701         minProgressWidth : 250,
2702         /**
2703          * An object containing the default button text strings that can be overriden for localized language support.
2704          * Supported properties are: ok, cancel, yes and no.
2705          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2706          * @type Object
2707          */
2708         buttonText : {
2709             ok : "OK",
2710             cancel : "Cancel",
2711             yes : "Yes",
2712             no : "No"
2713         }
2714     };
2715 }();
2716
2717 /**
2718  * Shorthand for {@link Roo.MessageBox}
2719  */
2720 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2721 Roo.Msg = Roo.Msg || Roo.MessageBox;
2722 /*
2723  * - LGPL
2724  *
2725  * navbar
2726  * 
2727  */
2728
2729 /**
2730  * @class Roo.bootstrap.Navbar
2731  * @extends Roo.bootstrap.Component
2732  * Bootstrap Navbar class
2733
2734  * @constructor
2735  * Create a new Navbar
2736  * @param {Object} config The config object
2737  */
2738
2739
2740 Roo.bootstrap.Navbar = function(config){
2741     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2742     
2743 };
2744
2745 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2746     
2747     
2748    
2749     // private
2750     navItems : false,
2751     loadMask : false,
2752     
2753     
2754     getAutoCreate : function(){
2755         
2756         
2757         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2758         
2759     },
2760     
2761     initEvents :function ()
2762     {
2763         //Roo.log(this.el.select('.navbar-toggle',true));
2764         this.el.select('.navbar-toggle',true).on('click', function() {
2765            // Roo.log('click');
2766             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2767         }, this);
2768         
2769         var mark = {
2770             tag: "div",
2771             cls:"x-dlg-mask"
2772         }
2773         
2774         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2775         
2776         var size = this.el.getSize();
2777         this.maskEl.setSize(size.width, size.height);
2778         this.maskEl.enableDisplayMode("block");
2779         this.maskEl.hide();
2780         
2781         if(this.loadMask){
2782             this.maskEl.show();
2783         }
2784     },
2785     
2786     
2787     getChildContainer : function()
2788     {
2789         if (this.el.select('.collapse').getCount()) {
2790             return this.el.select('.collapse',true).first();
2791         }
2792         
2793         return this.el;
2794     },
2795     
2796     mask : function()
2797     {
2798         this.maskEl.show();
2799     },
2800     
2801     unmask : function()
2802     {
2803         this.maskEl.hide();
2804     }
2805     
2806     
2807     
2808 });
2809
2810
2811
2812  
2813
2814  /*
2815  * - LGPL
2816  *
2817  * navbar
2818  * 
2819  */
2820
2821 /**
2822  * @class Roo.bootstrap.NavSimplebar
2823  * @extends Roo.bootstrap.Navbar
2824  * Bootstrap Sidebar class
2825  *
2826  * @cfg {Boolean} inverse is inverted color
2827  * 
2828  * @cfg {String} type (nav | pills | tabs)
2829  * @cfg {Boolean} arrangement stacked | justified
2830  * @cfg {String} align (left | right) alignment
2831  * 
2832  * @cfg {Boolean} main (true|false) main nav bar? default false
2833  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2834  * 
2835  * @cfg {String} tag (header|footer|nav|div) default is nav 
2836
2837  * 
2838  * 
2839  * 
2840  * @constructor
2841  * Create a new Sidebar
2842  * @param {Object} config The config object
2843  */
2844
2845
2846 Roo.bootstrap.NavSimplebar = function(config){
2847     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2848 };
2849
2850 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2851     
2852     inverse: false,
2853     
2854     type: false,
2855     arrangement: '',
2856     align : false,
2857     
2858     
2859     
2860     main : false,
2861     
2862     
2863     tag : false,
2864     
2865     
2866     getAutoCreate : function(){
2867         
2868         
2869         var cfg = {
2870             tag : this.tag || 'div',
2871             cls : 'navbar'
2872         };
2873           
2874         
2875         cfg.cn = [
2876             {
2877                 cls: 'nav',
2878                 tag : 'ul'
2879             }
2880         ];
2881         
2882          
2883         this.type = this.type || 'nav';
2884         if (['tabs','pills'].indexOf(this.type)!==-1) {
2885             cfg.cn[0].cls += ' nav-' + this.type
2886         
2887         
2888         } else {
2889             if (this.type!=='nav') {
2890                 Roo.log('nav type must be nav/tabs/pills')
2891             }
2892             cfg.cn[0].cls += ' navbar-nav'
2893         }
2894         
2895         
2896         
2897         
2898         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2899             cfg.cn[0].cls += ' nav-' + this.arrangement;
2900         }
2901         
2902         
2903         if (this.align === 'right') {
2904             cfg.cn[0].cls += ' navbar-right';
2905         }
2906         
2907         if (this.inverse) {
2908             cfg.cls += ' navbar-inverse';
2909             
2910         }
2911         
2912         
2913         return cfg;
2914     
2915         
2916     }
2917     
2918     
2919     
2920 });
2921
2922
2923
2924  
2925
2926  
2927        /*
2928  * - LGPL
2929  *
2930  * navbar
2931  * 
2932  */
2933
2934 /**
2935  * @class Roo.bootstrap.NavHeaderbar
2936  * @extends Roo.bootstrap.NavSimplebar
2937  * Bootstrap Sidebar class
2938  *
2939  * @cfg {String} brand what is brand
2940  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2941  * @cfg {String} brand_href href of the brand
2942  * 
2943  * @constructor
2944  * Create a new Sidebar
2945  * @param {Object} config The config object
2946  */
2947
2948
2949 Roo.bootstrap.NavHeaderbar = function(config){
2950     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2951 };
2952
2953 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2954     
2955     position: '',
2956     brand: '',
2957     brand_href: false,
2958     
2959     
2960     getAutoCreate : function(){
2961         
2962         
2963         
2964         var   cfg = {
2965             tag: this.nav || 'nav',
2966             cls: 'navbar',
2967             role: 'navigation',
2968             cn: [
2969                 {
2970                     tag: 'div',
2971                     cls: 'navbar-header',
2972                     cn: [
2973                         {
2974                         tag: 'button',
2975                         type: 'button',
2976                         cls: 'navbar-toggle',
2977                         'data-toggle': 'collapse',
2978                         cn: [
2979                             {
2980                                 tag: 'span',
2981                                 cls: 'sr-only',
2982                                 html: 'Toggle navigation'
2983                             },
2984                             {
2985                                 tag: 'span',
2986                                 cls: 'icon-bar'
2987                             },
2988                             {
2989                                 tag: 'span',
2990                                 cls: 'icon-bar'
2991                             },
2992                             {
2993                                 tag: 'span',
2994                                 cls: 'icon-bar'
2995                             }
2996                         ]
2997                         }
2998                     ]
2999                 },
3000                 {
3001                 tag: 'div',
3002                 cls: 'collapse navbar-collapse'
3003                 }
3004             ]
3005         };
3006         
3007         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3008         
3009         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3010             cfg.cls += ' navbar-' + this.position;
3011             
3012             // tag can override this..
3013             
3014             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3015         }
3016         
3017         if (this.brand !== '') {
3018             cfg.cn[0].cn.push({
3019                 tag: 'a',
3020                 href: this.brand_href ? this.brand_href : '#',
3021                 cls: 'navbar-brand',
3022                 cn: [
3023                 this.brand
3024                 ]
3025             });
3026         }
3027         
3028         if(this.main){
3029             cfg.cls += ' main-nav';
3030         }
3031         
3032         
3033         return cfg;
3034
3035         
3036     }
3037     
3038     
3039     
3040 });
3041
3042
3043
3044  
3045
3046  /*
3047  * - LGPL
3048  *
3049  * navbar
3050  * 
3051  */
3052
3053 /**
3054  * @class Roo.bootstrap.NavSidebar
3055  * @extends Roo.bootstrap.Navbar
3056  * Bootstrap Sidebar class
3057  * 
3058  * @constructor
3059  * Create a new Sidebar
3060  * @param {Object} config The config object
3061  */
3062
3063
3064 Roo.bootstrap.NavSidebar = function(config){
3065     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3066 };
3067
3068 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3069     
3070     sidebar : true, // used by Navbar Item at present...
3071     
3072     getAutoCreate : function(){
3073         
3074         
3075         return  {
3076             tag: 'div',
3077             cls: 'sidebar-nav'
3078         };
3079     
3080         
3081     }
3082     
3083     
3084     
3085 });
3086
3087
3088
3089  
3090
3091  /*
3092  * - LGPL
3093  *
3094  * nav group
3095  * 
3096  */
3097
3098 /**
3099  * @class Roo.bootstrap.NavGroup
3100  * @extends Roo.bootstrap.Component
3101  * Bootstrap NavGroup class
3102  * @cfg {String} align left | right
3103  * @cfg {Boolean} inverse false | true
3104  * @cfg {String} type (nav|pills|tab) default nav
3105  * @cfg {String} navId - reference Id for navbar.
3106
3107  * 
3108  * @constructor
3109  * Create a new nav group
3110  * @param {Object} config The config object
3111  */
3112
3113 Roo.bootstrap.NavGroup = function(config){
3114     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3115     this.navItems = [];
3116     Roo.bootstrap.NavGroup.register(this);
3117      this.addEvents({
3118         /**
3119              * @event changed
3120              * Fires when the active item changes
3121              * @param {Roo.bootstrap.NavGroup} this
3122              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3123              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3124          */
3125         'changed': true
3126      });
3127     
3128 };
3129
3130 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3131     
3132     align: '',
3133     inverse: false,
3134     form: false,
3135     type: 'nav',
3136     navId : '',
3137     // private
3138     
3139     navItems : false,
3140     
3141     getAutoCreate : function()
3142     {
3143         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3144         
3145         cfg = {
3146             tag : 'ul',
3147             cls: 'nav' 
3148         }
3149         
3150         if (['tabs','pills'].indexOf(this.type)!==-1) {
3151             cfg.cls += ' nav-' + this.type
3152         } else {
3153             if (this.type!=='nav') {
3154                 Roo.log('nav type must be nav/tabs/pills')
3155             }
3156             cfg.cls += ' navbar-nav'
3157         }
3158         
3159         if (this.parent() instanceof Roo.bootstrap.NavSidebar) {
3160             cfg = {
3161                 tag: 'ul',
3162                 cls: 'dashboard-menu'
3163             }
3164             
3165             return cfg;
3166         }
3167         
3168         if (this.form === true) {
3169             cfg = {
3170                 tag: 'form',
3171                 cls: 'navbar-form'
3172             }
3173             
3174             if (this.align === 'right') {
3175                 cfg.cls += ' navbar-right';
3176             } else {
3177                 cfg.cls += ' navbar-left';
3178             }
3179         }
3180         
3181         if (this.align === 'right') {
3182             cfg.cls += ' navbar-right';
3183         }
3184         
3185         if (this.inverse) {
3186             cfg.cls += ' navbar-inverse';
3187             
3188         }
3189         
3190         
3191         return cfg;
3192     },
3193     
3194     setActiveItem : function(item)
3195     {
3196         var prev = false;
3197         Roo.each(this.navItems, function(v){
3198             if (v == item) {
3199                 return ;
3200             }
3201             if (v.isActive()) {
3202                 v.setActive(false, true);
3203                 prev = v;
3204                 
3205             }
3206             
3207         });
3208
3209         item.setActive(true, true);
3210         this.fireEvent('changed', this, item, prev);
3211         
3212         
3213     },
3214     
3215     
3216     register : function(item)
3217     {
3218         this.navItems.push( item);
3219         item.navId = this.navId;
3220     
3221     },
3222     getNavItem: function(tabId)
3223     {
3224         var ret = false;
3225         Roo.each(this.navItems, function(e) {
3226             if (e.tabId == tabId) {
3227                ret =  e;
3228                return false;
3229             }
3230             return true;
3231             
3232         });
3233         return ret;
3234     }
3235 });
3236
3237  
3238 Roo.apply(Roo.bootstrap.NavGroup, {
3239     
3240     groups: {},
3241     
3242     register : function(navgrp)
3243     {
3244         this.groups[navgrp.navId] = navgrp;
3245         
3246     },
3247     get: function(navId) {
3248         return this.groups[navId];
3249     }
3250     
3251     
3252     
3253 });
3254
3255  /*
3256  * - LGPL
3257  *
3258  * row
3259  * 
3260  */
3261
3262 /**
3263  * @class Roo.bootstrap.Navbar.Item
3264  * @extends Roo.bootstrap.Component
3265  * Bootstrap Navbar.Button class
3266  * @cfg {String} href  link to
3267  * @cfg {String} html content of button
3268  * @cfg {String} badge text inside badge
3269  * @cfg {String} glyphicon name of glyphicon
3270  * @cfg {String} icon name of font awesome icon
3271  * @cfg {Boolean} active Is item active
3272  * @cfg {Boolean} preventDefault (true | false) default false
3273  * @cfg {String} tabId the tab that this item activates.
3274   
3275  * @constructor
3276  * Create a new Navbar Button
3277  * @param {Object} config The config object
3278  */
3279 Roo.bootstrap.Navbar.Item = function(config){
3280     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3281     this.addEvents({
3282         // raw events
3283         /**
3284          * @event click
3285          * The raw click event for the entire grid.
3286          * @param {Roo.EventObject} e
3287          */
3288         "click" : true,
3289          /**
3290             * @event changed
3291             * Fires when the active item active state changes
3292             * @param {Roo.bootstrap.Navbar.Item} this
3293             * @param {boolean} state the new state
3294              
3295          */
3296         'changed': true
3297     });
3298    
3299 };
3300
3301 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3302     
3303     href: false,
3304     html: '',
3305     badge: '',
3306     icon: false,
3307     glyphicon: false,
3308     active: false,
3309     preventDefault : false,
3310     tabId : false,
3311     
3312     getAutoCreate : function(){
3313         
3314         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3315         
3316         if (this.parent().parent().sidebar === true) {
3317             cfg = {
3318                 tag: 'li',
3319                 cls: '',
3320                 cn: [
3321                     {
3322                     tag: 'p',
3323                     cls: ''
3324                     }
3325                 ]
3326             }
3327             
3328             if (this.html) {
3329                 cfg.cn[0].html = this.html;
3330             }
3331             
3332             if (this.active) {
3333                 this.cls += ' active';
3334             }
3335             
3336             if (this.menu) {
3337                 cfg.cn[0].cls += ' dropdown-toggle';
3338                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3339             }
3340             
3341             if (this.href) {
3342                 cfg.cn[0].tag = 'a',
3343                 cfg.cn[0].href = this.href;
3344             }
3345             
3346             if (this.glyphicon) {
3347                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3348             }
3349                 
3350             if (this.icon) {
3351                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3352             }
3353             
3354             return cfg;
3355         }
3356         
3357         cfg = {
3358             tag: 'li',
3359                 cls: 'nav-item'
3360         }
3361             
3362         if (this.active) {
3363             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3364         }
3365             
3366         cfg.cn = [
3367             {
3368                 tag: 'p',
3369                 html: 'Text'
3370             }
3371         ];
3372         
3373         if (this.glyphicon) {
3374             if(cfg.html){cfg.html = ' ' + this.html};
3375             cfg.cn=[
3376                 {
3377                     tag: 'span',
3378                     cls: 'glyphicon glyphicon-' + this.glyphicon
3379                 }
3380             ];
3381         }
3382         
3383         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3384         
3385         if (this.menu) {
3386             cfg.cn[0].tag='a';
3387             cfg.cn[0].href='#';
3388             cfg.cn[0].html += " <span class='caret'></span>";
3389         //}else if (!this.href) {
3390         //    cfg.cn[0].tag='p';
3391         //    cfg.cn[0].cls='navbar-text';
3392         } else {
3393             cfg.cn[0].tag='a';
3394             cfg.cn[0].href=this.href||'#';
3395             cfg.cn[0].html=this.html;
3396         }
3397         
3398         if (this.badge !== '') {
3399             
3400             cfg.cn[0].cn=[
3401             cfg.cn[0].html + ' ',
3402             {
3403                 tag: 'span',
3404                 cls: 'badge',
3405                 html: this.badge
3406             }
3407             ];
3408             cfg.cn[0].html=''
3409         }
3410          
3411         if (this.icon) {
3412             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3413         }
3414         
3415         return cfg;
3416     },
3417     initEvents: function() {
3418        // Roo.log('init events?');
3419        // Roo.log(this.el.dom);
3420         this.el.select('a',true).on('click', this.onClick, this);
3421         // at this point parent should be available..
3422         this.parent().register(this);
3423     },
3424     
3425     onClick : function(e)
3426     {
3427         if(this.preventDefault){
3428             e.preventDefault();
3429         }
3430         
3431         if(this.fireEvent('click', this, e) === false){
3432             return;
3433         };
3434         
3435         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3436              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3437                 this.parent().setActiveItem(this);
3438             }
3439             
3440             
3441             
3442         } 
3443     },
3444     
3445     isActive: function () {
3446         return this.active
3447     },
3448     setActive : function(state, fire)
3449     {
3450         this.active = state;
3451         if (!state ) {
3452             this.el.removeClass('active');
3453         } else if (!this.el.hasClass('active')) {
3454             this.el.addClass('active');
3455         }
3456         if (fire) {
3457             this.fireEvent('changed', this, state);
3458         }
3459         
3460         
3461     }
3462      // this should not be here...
3463  
3464 });
3465  
3466
3467  /*
3468  * - LGPL
3469  *
3470  * row
3471  * 
3472  */
3473
3474 /**
3475  * @class Roo.bootstrap.Row
3476  * @extends Roo.bootstrap.Component
3477  * Bootstrap Row class (contains columns...)
3478  * 
3479  * @constructor
3480  * Create a new Row
3481  * @param {Object} config The config object
3482  */
3483
3484 Roo.bootstrap.Row = function(config){
3485     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3486 };
3487
3488 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3489     
3490     getAutoCreate : function(){
3491        return {
3492             cls: 'row clearfix'
3493        };
3494     }
3495     
3496     
3497 });
3498
3499  
3500
3501  /*
3502  * - LGPL
3503  *
3504  * element
3505  * 
3506  */
3507
3508 /**
3509  * @class Roo.bootstrap.Element
3510  * @extends Roo.bootstrap.Component
3511  * Bootstrap Element class
3512  * @cfg {String} html contents of the element
3513  * @cfg {String} tag tag of the element
3514  * @cfg {String} cls class of the element
3515  * 
3516  * @constructor
3517  * Create a new Element
3518  * @param {Object} config The config object
3519  */
3520
3521 Roo.bootstrap.Element = function(config){
3522     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3523 };
3524
3525 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3526     
3527     tag: 'div',
3528     cls: '',
3529     html: '',
3530      
3531     
3532     getAutoCreate : function(){
3533         
3534         var cfg = {
3535             tag: this.tag,
3536             cls: this.cls,
3537             html: this.html
3538         }
3539         
3540         
3541         
3542         return cfg;
3543     }
3544    
3545 });
3546
3547  
3548
3549  /*
3550  * - LGPL
3551  *
3552  * pagination
3553  * 
3554  */
3555
3556 /**
3557  * @class Roo.bootstrap.Pagination
3558  * @extends Roo.bootstrap.Component
3559  * Bootstrap Pagination class
3560  * @cfg {String} size xs | sm | md | lg
3561  * @cfg {Boolean} inverse false | true
3562  * 
3563  * @constructor
3564  * Create a new Pagination
3565  * @param {Object} config The config object
3566  */
3567
3568 Roo.bootstrap.Pagination = function(config){
3569     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3570 };
3571
3572 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3573     
3574     cls: false,
3575     size: false,
3576     inverse: false,
3577     
3578     getAutoCreate : function(){
3579         var cfg = {
3580             tag: 'ul',
3581                 cls: 'pagination'
3582         };
3583         if (this.inverse) {
3584             cfg.cls += ' inverse';
3585         }
3586         if (this.html) {
3587             cfg.html=this.html;
3588         }
3589         if (this.cls) {
3590             cfg.cls += " " + this.cls;
3591         }
3592         return cfg;
3593     }
3594    
3595 });
3596
3597  
3598
3599  /*
3600  * - LGPL
3601  *
3602  * Pagination item
3603  * 
3604  */
3605
3606
3607 /**
3608  * @class Roo.bootstrap.PaginationItem
3609  * @extends Roo.bootstrap.Component
3610  * Bootstrap PaginationItem class
3611  * @cfg {String} html text
3612  * @cfg {String} href the link
3613  * @cfg {Boolean} preventDefault (true | false) default true
3614  * @cfg {Boolean} active (true | false) default false
3615  * 
3616  * 
3617  * @constructor
3618  * Create a new PaginationItem
3619  * @param {Object} config The config object
3620  */
3621
3622
3623 Roo.bootstrap.PaginationItem = function(config){
3624     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3625     this.addEvents({
3626         // raw events
3627         /**
3628          * @event click
3629          * The raw click event for the entire grid.
3630          * @param {Roo.EventObject} e
3631          */
3632         "click" : true
3633     });
3634 };
3635
3636 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3637     
3638     href : false,
3639     html : false,
3640     preventDefault: true,
3641     active : false,
3642     cls : false,
3643     
3644     getAutoCreate : function(){
3645         var cfg= {
3646             tag: 'li',
3647             cn: [
3648                 {
3649                     tag : 'a',
3650                     href : this.href ? this.href : '#',
3651                     html : this.html ? this.html : ''
3652                 }
3653             ]
3654         };
3655         
3656         if(this.cls){
3657             cfg.cls = this.cls;
3658         }
3659         
3660         if(this.active){
3661             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3662         }
3663         
3664         return cfg;
3665     },
3666     
3667     initEvents: function() {
3668         
3669         this.el.on('click', this.onClick, this);
3670         
3671     },
3672     onClick : function(e)
3673     {
3674         Roo.log('PaginationItem on click ');
3675         if(this.preventDefault){
3676             e.preventDefault();
3677         }
3678         
3679         this.fireEvent('click', this, e);
3680     }
3681    
3682 });
3683
3684  
3685
3686  /*
3687  * - LGPL
3688  *
3689  * slider
3690  * 
3691  */
3692
3693
3694 /**
3695  * @class Roo.bootstrap.Slider
3696  * @extends Roo.bootstrap.Component
3697  * Bootstrap Slider class
3698  *    
3699  * @constructor
3700  * Create a new Slider
3701  * @param {Object} config The config object
3702  */
3703
3704 Roo.bootstrap.Slider = function(config){
3705     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3706 };
3707
3708 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3709     
3710     getAutoCreate : function(){
3711         
3712         var cfg = {
3713             tag: 'div',
3714             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3715             cn: [
3716                 {
3717                     tag: 'a',
3718                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3719                 }
3720             ]
3721         }
3722         
3723         return cfg;
3724     }
3725    
3726 });
3727
3728  /*
3729  * - LGPL
3730  *
3731  * table
3732  * 
3733  */
3734
3735 /**
3736  * @class Roo.bootstrap.Table
3737  * @extends Roo.bootstrap.Component
3738  * Bootstrap Table class
3739  * @cfg {String} cls table class
3740  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3741  * @cfg {String} bgcolor Specifies the background color for a table
3742  * @cfg {Number} border Specifies whether the table cells should have borders or not
3743  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3744  * @cfg {Number} cellspacing Specifies the space between cells
3745  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3746  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3747  * @cfg {String} sortable Specifies that the table should be sortable
3748  * @cfg {String} summary Specifies a summary of the content of a table
3749  * @cfg {Number} width Specifies the width of a table
3750  * 
3751  * @cfg {boolean} striped Should the rows be alternative striped
3752  * @cfg {boolean} bordered Add borders to the table
3753  * @cfg {boolean} hover Add hover highlighting
3754  * @cfg {boolean} condensed Format condensed
3755  * @cfg {boolean} responsive Format condensed
3756  *
3757  
3758  
3759  * 
3760  * @constructor
3761  * Create a new Table
3762  * @param {Object} config The config object
3763  */
3764
3765 Roo.bootstrap.Table = function(config){
3766     Roo.bootstrap.Table.superclass.constructor.call(this, config);
3767     
3768     if (this.sm) {
3769         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3770         this.sm = this.selModel;
3771         this.sm.xmodule = this.xmodule || false;
3772     }
3773     if (this.cm && typeof(this.cm.config) == 'undefined') {
3774         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
3775         this.cm = this.colModel;
3776         this.cm.xmodule = this.xmodule || false;
3777     }
3778     if (this.store) {
3779         this.store= Roo.factory(this.store, Roo.data);
3780         this.ds = this.store;
3781         this.ds.xmodule = this.xmodule || false;
3782          
3783     }
3784 };
3785
3786 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
3787     
3788     cls: false,
3789     align: false,
3790     bgcolor: false,
3791     border: false,
3792     cellpadding: false,
3793     cellspacing: false,
3794     frame: false,
3795     rules: false,
3796     sortable: false,
3797     summary: false,
3798     width: false,
3799     striped : false,
3800     bordered: false,
3801     hover:  false,
3802     condensed : false,
3803     responsive : false,
3804     sm : false,
3805     cm : false,
3806     store : false,
3807     
3808     getAutoCreate : function(){
3809         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3810         
3811         cfg = {
3812             tag: 'table',
3813             cls : 'table',
3814             cn : []
3815         }
3816             
3817         if (this.striped) {
3818             cfg.cls += ' table-striped';
3819         }
3820         if (this.hover) {
3821             cfg.cls += ' table-hover';
3822         }
3823         if (this.bordered) {
3824             cfg.cls += ' table-bordered';
3825         }
3826         if (this.condensed) {
3827             cfg.cls += ' table-condensed';
3828         }
3829         if (this.responsive) {
3830             cfg.cls += ' table-responsive';
3831         }
3832         
3833           
3834         
3835         
3836         if (this.cls) {
3837             cfg.cls+=  ' ' +this.cls;
3838         }
3839         
3840         // this lot should be simplifed...
3841         
3842         if (this.align) {
3843             cfg.align=this.align;
3844         }
3845         if (this.bgcolor) {
3846             cfg.bgcolor=this.bgcolor;
3847         }
3848         if (this.border) {
3849             cfg.border=this.border;
3850         }
3851         if (this.cellpadding) {
3852             cfg.cellpadding=this.cellpadding;
3853         }
3854         if (this.cellspacing) {
3855             cfg.cellspacing=this.cellspacing;
3856         }
3857         if (this.frame) {
3858             cfg.frame=this.frame;
3859         }
3860         if (this.rules) {
3861             cfg.rules=this.rules;
3862         }
3863         if (this.sortable) {
3864             cfg.sortable=this.sortable;
3865         }
3866         if (this.summary) {
3867             cfg.summary=this.summary;
3868         }
3869         if (this.width) {
3870             cfg.width=this.width;
3871         }
3872         
3873         if(this.store || this.cm){
3874             cfg.cn.push(this.renderHeader());
3875             cfg.cn.push(this.renderBody());
3876             cfg.cn.push(this.renderFooter());
3877             
3878             cfg.cls+=  ' TableGrid';
3879         }
3880         
3881         return cfg;
3882     },
3883 //    
3884 //    initTableGrid : function()
3885 //    {
3886 //        var cfg = {};
3887 //        
3888 //        var header = {
3889 //            tag: 'thead',
3890 //            cn : []
3891 //        };
3892 //        
3893 //        var cm = this.cm;
3894 //        
3895 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3896 //            header.cn.push({
3897 //                tag: 'th',
3898 //                html: cm.getColumnHeader(i)
3899 //            })
3900 //        }
3901 //        
3902 //        cfg.push(header);
3903 //        
3904 //        return cfg;
3905 //        
3906 //        
3907 //    },
3908     
3909     initEvents : function()
3910     {   
3911         if(!this.store || !this.cm){
3912             return;
3913         }
3914         
3915         Roo.log('initEvents with ds!!!!');
3916         
3917         var _this = this;
3918         
3919         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3920             e.on('click', _this.sort, _this);
3921         });
3922 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3923 //        this.maskEl.enableDisplayMode("block");
3924 //        this.maskEl.show();
3925         
3926         this.store.on('load', this.onLoad, this);
3927         this.store.on('beforeload', this.onBeforeLoad, this);
3928         
3929         this.store.load();
3930         
3931         
3932         
3933     },
3934     
3935     sort : function(e,el)
3936     {
3937         var col = Roo.get(el)
3938         
3939         if(!col.hasClass('sortable')){
3940             return;
3941         }
3942         
3943         var sort = col.attr('sort');
3944         var dir = 'ASC';
3945         
3946         if(col.hasClass('glyphicon-arrow-up')){
3947             dir = 'DESC';
3948         }
3949         
3950         this.store.sortInfo = {field : sort, direction : dir};
3951         
3952         this.store.load();
3953     },
3954     
3955     renderHeader : function()
3956     {
3957         var header = {
3958             tag: 'thead',
3959             cn : []
3960         };
3961         
3962         var cm = this.cm;
3963         
3964         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3965             
3966             var config = cm.config[i];
3967             
3968             var c = {
3969                 tag: 'th',
3970                 html: cm.getColumnHeader(i)
3971             };
3972             
3973             if(typeof(config.dataIndex) != 'undefined'){
3974                 c.sort = config.dataIndex;
3975             }
3976             
3977             if(typeof(config.sortable) != 'undefined' && config.sortable){
3978                 c.cls = 'sortable';
3979             }
3980             
3981             if(typeof(config.width) != 'undefined'){
3982                 c.style = 'width:' + config.width + 'px';
3983             }
3984             
3985             header.cn.push(c)
3986         }
3987         
3988         return header;
3989     },
3990     
3991     renderBody : function()
3992     {
3993         var body = {
3994             tag: 'tbody',
3995             cn : []
3996         };
3997         
3998         return body;
3999     },
4000     
4001     renderFooter : function()
4002     {
4003         var footer = {
4004             tag: 'tfoot',
4005             cn : []
4006         };
4007         
4008         return footer;
4009     },
4010     
4011     onLoad : function()
4012     {
4013         Roo.log('ds onload');
4014         
4015         var _this = this;
4016         var cm = this.cm;
4017         
4018         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4019             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4020             
4021             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4022                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4023             }
4024             
4025             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4026                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4027             }
4028         });
4029         
4030         var tbody = this.el.select('tbody', true).first();
4031         
4032         var renders = [];
4033         
4034         if(this.store.getCount() > 0){
4035             this.store.data.each(function(d){
4036                 var row = {
4037                     tag : 'tr',
4038                     cn : []
4039                 };
4040                 
4041                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4042                     var renderer = cm.getRenderer(i);
4043                     var config = cm.config[i];
4044                     var value = '';
4045                     var id = Roo.id();
4046                     
4047                     if(typeof(renderer) !== 'undefined'){
4048                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4049                     }
4050                     
4051                     if(typeof(value) === 'object'){
4052                         renders.push({
4053                             id : id,
4054                             cfg : value 
4055                         })
4056                     }
4057                     
4058                     var td = {
4059                         tag: 'td',
4060                         id: id,
4061                         html: (typeof(value) === 'object') ? '' : value
4062                     };
4063                     
4064                     if(typeof(config.width) != 'undefined'){
4065                         td.style = 'width:' +  config.width + 'px';
4066                     }
4067                     
4068                     row.cn.push(td);
4069                    
4070                 }
4071                 
4072                 tbody.createChild(row);
4073                 
4074             });
4075         }
4076         
4077         
4078         if(renders.length){
4079             var _this = this;
4080             Roo.each(renders, function(r){
4081                 _this.renderColumn(r);
4082             })
4083         }
4084 //        
4085 //        if(this.loadMask){
4086 //            this.maskEl.hide();
4087 //        }
4088     },
4089     
4090     onBeforeLoad : function()
4091     {
4092         Roo.log('ds onBeforeLoad');
4093         
4094         this.clear();
4095         
4096 //        if(this.loadMask){
4097 //            this.maskEl.show();
4098 //        }
4099     },
4100     
4101     clear : function()
4102     {
4103         this.el.select('tbody', true).first().dom.innerHTML = '';
4104     },
4105     
4106     getSelectionModel : function(){
4107         if(!this.selModel){
4108             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4109         }
4110         return this.selModel;
4111     },
4112     
4113     renderColumn : function(r)
4114     {
4115         var _this = this;
4116         r.cfg.render(Roo.get(r.id));
4117         
4118         if(r.cfg.cn){
4119             Roo.each(r.cfg.cn, function(c){
4120                 var child = {
4121                     id: r.id,
4122                     cfg: c
4123                 }
4124                 _this.renderColumn(child);
4125             })
4126         }
4127     }
4128    
4129 });
4130
4131  
4132
4133  /*
4134  * - LGPL
4135  *
4136  * table cell
4137  * 
4138  */
4139
4140 /**
4141  * @class Roo.bootstrap.TableCell
4142  * @extends Roo.bootstrap.Component
4143  * Bootstrap TableCell class
4144  * @cfg {String} html cell contain text
4145  * @cfg {String} cls cell class
4146  * @cfg {String} tag cell tag (td|th) default td
4147  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4148  * @cfg {String} align Aligns the content in a cell
4149  * @cfg {String} axis Categorizes cells
4150  * @cfg {String} bgcolor Specifies the background color of a cell
4151  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4152  * @cfg {Number} colspan Specifies the number of columns a cell should span
4153  * @cfg {String} headers Specifies one or more header cells a cell is related to
4154  * @cfg {Number} height Sets the height of a cell
4155  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4156  * @cfg {Number} rowspan Sets the number of rows a cell should span
4157  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4158  * @cfg {String} valign Vertical aligns the content in a cell
4159  * @cfg {Number} width Specifies the width of a cell
4160  * 
4161  * @constructor
4162  * Create a new TableCell
4163  * @param {Object} config The config object
4164  */
4165
4166 Roo.bootstrap.TableCell = function(config){
4167     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4168 };
4169
4170 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4171     
4172     html: false,
4173     cls: false,
4174     tag: false,
4175     abbr: false,
4176     align: false,
4177     axis: false,
4178     bgcolor: false,
4179     charoff: false,
4180     colspan: false,
4181     headers: false,
4182     height: false,
4183     nowrap: false,
4184     rowspan: false,
4185     scope: false,
4186     valign: false,
4187     width: false,
4188     
4189     
4190     getAutoCreate : function(){
4191         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4192         
4193         cfg = {
4194             tag: 'td'
4195         }
4196         
4197         if(this.tag){
4198             cfg.tag = this.tag;
4199         }
4200         
4201         if (this.html) {
4202             cfg.html=this.html
4203         }
4204         if (this.cls) {
4205             cfg.cls=this.cls
4206         }
4207         if (this.abbr) {
4208             cfg.abbr=this.abbr
4209         }
4210         if (this.align) {
4211             cfg.align=this.align
4212         }
4213         if (this.axis) {
4214             cfg.axis=this.axis
4215         }
4216         if (this.bgcolor) {
4217             cfg.bgcolor=this.bgcolor
4218         }
4219         if (this.charoff) {
4220             cfg.charoff=this.charoff
4221         }
4222         if (this.colspan) {
4223             cfg.colspan=this.colspan
4224         }
4225         if (this.headers) {
4226             cfg.headers=this.headers
4227         }
4228         if (this.height) {
4229             cfg.height=this.height
4230         }
4231         if (this.nowrap) {
4232             cfg.nowrap=this.nowrap
4233         }
4234         if (this.rowspan) {
4235             cfg.rowspan=this.rowspan
4236         }
4237         if (this.scope) {
4238             cfg.scope=this.scope
4239         }
4240         if (this.valign) {
4241             cfg.valign=this.valign
4242         }
4243         if (this.width) {
4244             cfg.width=this.width
4245         }
4246         
4247         
4248         return cfg;
4249     }
4250    
4251 });
4252
4253  
4254
4255  /*
4256  * - LGPL
4257  *
4258  * table row
4259  * 
4260  */
4261
4262 /**
4263  * @class Roo.bootstrap.TableRow
4264  * @extends Roo.bootstrap.Component
4265  * Bootstrap TableRow class
4266  * @cfg {String} cls row class
4267  * @cfg {String} align Aligns the content in a table row
4268  * @cfg {String} bgcolor Specifies a background color for a table row
4269  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4270  * @cfg {String} valign Vertical aligns the content in a table row
4271  * 
4272  * @constructor
4273  * Create a new TableRow
4274  * @param {Object} config The config object
4275  */
4276
4277 Roo.bootstrap.TableRow = function(config){
4278     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4279 };
4280
4281 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4282     
4283     cls: false,
4284     align: false,
4285     bgcolor: false,
4286     charoff: false,
4287     valign: false,
4288     
4289     getAutoCreate : function(){
4290         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4291         
4292         cfg = {
4293             tag: 'tr'
4294         }
4295             
4296         if(this.cls){
4297             cfg.cls = this.cls;
4298         }
4299         if(this.align){
4300             cfg.align = this.align;
4301         }
4302         if(this.bgcolor){
4303             cfg.bgcolor = this.bgcolor;
4304         }
4305         if(this.charoff){
4306             cfg.charoff = this.charoff;
4307         }
4308         if(this.valign){
4309             cfg.valign = this.valign;
4310         }
4311         
4312         return cfg;
4313     }
4314    
4315 });
4316
4317  
4318
4319  /*
4320  * - LGPL
4321  *
4322  * table body
4323  * 
4324  */
4325
4326 /**
4327  * @class Roo.bootstrap.TableBody
4328  * @extends Roo.bootstrap.Component
4329  * Bootstrap TableBody class
4330  * @cfg {String} cls element class
4331  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4332  * @cfg {String} align Aligns the content inside the element
4333  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4334  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4335  * 
4336  * @constructor
4337  * Create a new TableBody
4338  * @param {Object} config The config object
4339  */
4340
4341 Roo.bootstrap.TableBody = function(config){
4342     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4343 };
4344
4345 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4346     
4347     cls: false,
4348     tag: false,
4349     align: false,
4350     charoff: false,
4351     valign: false,
4352     
4353     getAutoCreate : function(){
4354         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4355         
4356         cfg = {
4357             tag: 'tbody'
4358         }
4359             
4360         if (this.cls) {
4361             cfg.cls=this.cls
4362         }
4363         if(this.tag){
4364             cfg.tag = this.tag;
4365         }
4366         
4367         if(this.align){
4368             cfg.align = this.align;
4369         }
4370         if(this.charoff){
4371             cfg.charoff = this.charoff;
4372         }
4373         if(this.valign){
4374             cfg.valign = this.valign;
4375         }
4376         
4377         return cfg;
4378     }
4379     
4380     
4381 //    initEvents : function()
4382 //    {
4383 //        
4384 //        if(!this.store){
4385 //            return;
4386 //        }
4387 //        
4388 //        this.store = Roo.factory(this.store, Roo.data);
4389 //        this.store.on('load', this.onLoad, this);
4390 //        
4391 //        this.store.load();
4392 //        
4393 //    },
4394 //    
4395 //    onLoad: function () 
4396 //    {   
4397 //        this.fireEvent('load', this);
4398 //    }
4399 //    
4400 //   
4401 });
4402
4403  
4404
4405  /*
4406  * Based on:
4407  * Ext JS Library 1.1.1
4408  * Copyright(c) 2006-2007, Ext JS, LLC.
4409  *
4410  * Originally Released Under LGPL - original licence link has changed is not relivant.
4411  *
4412  * Fork - LGPL
4413  * <script type="text/javascript">
4414  */
4415
4416 // as we use this in bootstrap.
4417 Roo.namespace('Roo.form');
4418  /**
4419  * @class Roo.form.Action
4420  * Internal Class used to handle form actions
4421  * @constructor
4422  * @param {Roo.form.BasicForm} el The form element or its id
4423  * @param {Object} config Configuration options
4424  */
4425
4426  
4427  
4428 // define the action interface
4429 Roo.form.Action = function(form, options){
4430     this.form = form;
4431     this.options = options || {};
4432 };
4433 /**
4434  * Client Validation Failed
4435  * @const 
4436  */
4437 Roo.form.Action.CLIENT_INVALID = 'client';
4438 /**
4439  * Server Validation Failed
4440  * @const 
4441  */
4442 Roo.form.Action.SERVER_INVALID = 'server';
4443  /**
4444  * Connect to Server Failed
4445  * @const 
4446  */
4447 Roo.form.Action.CONNECT_FAILURE = 'connect';
4448 /**
4449  * Reading Data from Server Failed
4450  * @const 
4451  */
4452 Roo.form.Action.LOAD_FAILURE = 'load';
4453
4454 Roo.form.Action.prototype = {
4455     type : 'default',
4456     failureType : undefined,
4457     response : undefined,
4458     result : undefined,
4459
4460     // interface method
4461     run : function(options){
4462
4463     },
4464
4465     // interface method
4466     success : function(response){
4467
4468     },
4469
4470     // interface method
4471     handleResponse : function(response){
4472
4473     },
4474
4475     // default connection failure
4476     failure : function(response){
4477         
4478         this.response = response;
4479         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4480         this.form.afterAction(this, false);
4481     },
4482
4483     processResponse : function(response){
4484         this.response = response;
4485         if(!response.responseText){
4486             return true;
4487         }
4488         this.result = this.handleResponse(response);
4489         return this.result;
4490     },
4491
4492     // utility functions used internally
4493     getUrl : function(appendParams){
4494         var url = this.options.url || this.form.url || this.form.el.dom.action;
4495         if(appendParams){
4496             var p = this.getParams();
4497             if(p){
4498                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4499             }
4500         }
4501         return url;
4502     },
4503
4504     getMethod : function(){
4505         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4506     },
4507
4508     getParams : function(){
4509         var bp = this.form.baseParams;
4510         var p = this.options.params;
4511         if(p){
4512             if(typeof p == "object"){
4513                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4514             }else if(typeof p == 'string' && bp){
4515                 p += '&' + Roo.urlEncode(bp);
4516             }
4517         }else if(bp){
4518             p = Roo.urlEncode(bp);
4519         }
4520         return p;
4521     },
4522
4523     createCallback : function(){
4524         return {
4525             success: this.success,
4526             failure: this.failure,
4527             scope: this,
4528             timeout: (this.form.timeout*1000),
4529             upload: this.form.fileUpload ? this.success : undefined
4530         };
4531     }
4532 };
4533
4534 Roo.form.Action.Submit = function(form, options){
4535     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4536 };
4537
4538 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4539     type : 'submit',
4540
4541     haveProgress : false,
4542     uploadComplete : false,
4543     
4544     // uploadProgress indicator.
4545     uploadProgress : function()
4546     {
4547         if (!this.form.progressUrl) {
4548             return;
4549         }
4550         
4551         if (!this.haveProgress) {
4552             Roo.MessageBox.progress("Uploading", "Uploading");
4553         }
4554         if (this.uploadComplete) {
4555            Roo.MessageBox.hide();
4556            return;
4557         }
4558         
4559         this.haveProgress = true;
4560    
4561         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4562         
4563         var c = new Roo.data.Connection();
4564         c.request({
4565             url : this.form.progressUrl,
4566             params: {
4567                 id : uid
4568             },
4569             method: 'GET',
4570             success : function(req){
4571                //console.log(data);
4572                 var rdata = false;
4573                 var edata;
4574                 try  {
4575                    rdata = Roo.decode(req.responseText)
4576                 } catch (e) {
4577                     Roo.log("Invalid data from server..");
4578                     Roo.log(edata);
4579                     return;
4580                 }
4581                 if (!rdata || !rdata.success) {
4582                     Roo.log(rdata);
4583                     Roo.MessageBox.alert(Roo.encode(rdata));
4584                     return;
4585                 }
4586                 var data = rdata.data;
4587                 
4588                 if (this.uploadComplete) {
4589                    Roo.MessageBox.hide();
4590                    return;
4591                 }
4592                    
4593                 if (data){
4594                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4595                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4596                     );
4597                 }
4598                 this.uploadProgress.defer(2000,this);
4599             },
4600        
4601             failure: function(data) {
4602                 Roo.log('progress url failed ');
4603                 Roo.log(data);
4604             },
4605             scope : this
4606         });
4607            
4608     },
4609     
4610     
4611     run : function()
4612     {
4613         // run get Values on the form, so it syncs any secondary forms.
4614         this.form.getValues();
4615         
4616         var o = this.options;
4617         var method = this.getMethod();
4618         var isPost = method == 'POST';
4619         if(o.clientValidation === false || this.form.isValid()){
4620             
4621             if (this.form.progressUrl) {
4622                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4623                     (new Date() * 1) + '' + Math.random());
4624                     
4625             } 
4626             
4627             
4628             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4629                 form:this.form.el.dom,
4630                 url:this.getUrl(!isPost),
4631                 method: method,
4632                 params:isPost ? this.getParams() : null,
4633                 isUpload: this.form.fileUpload
4634             }));
4635             
4636             this.uploadProgress();
4637
4638         }else if (o.clientValidation !== false){ // client validation failed
4639             this.failureType = Roo.form.Action.CLIENT_INVALID;
4640             this.form.afterAction(this, false);
4641         }
4642     },
4643
4644     success : function(response)
4645     {
4646         this.uploadComplete= true;
4647         if (this.haveProgress) {
4648             Roo.MessageBox.hide();
4649         }
4650         
4651         
4652         var result = this.processResponse(response);
4653         if(result === true || result.success){
4654             this.form.afterAction(this, true);
4655             return;
4656         }
4657         if(result.errors){
4658             this.form.markInvalid(result.errors);
4659             this.failureType = Roo.form.Action.SERVER_INVALID;
4660         }
4661         this.form.afterAction(this, false);
4662     },
4663     failure : function(response)
4664     {
4665         this.uploadComplete= true;
4666         if (this.haveProgress) {
4667             Roo.MessageBox.hide();
4668         }
4669         
4670         this.response = response;
4671         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4672         this.form.afterAction(this, false);
4673     },
4674     
4675     handleResponse : function(response){
4676         if(this.form.errorReader){
4677             var rs = this.form.errorReader.read(response);
4678             var errors = [];
4679             if(rs.records){
4680                 for(var i = 0, len = rs.records.length; i < len; i++) {
4681                     var r = rs.records[i];
4682                     errors[i] = r.data;
4683                 }
4684             }
4685             if(errors.length < 1){
4686                 errors = null;
4687             }
4688             return {
4689                 success : rs.success,
4690                 errors : errors
4691             };
4692         }
4693         var ret = false;
4694         try {
4695             ret = Roo.decode(response.responseText);
4696         } catch (e) {
4697             ret = {
4698                 success: false,
4699                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4700                 errors : []
4701             };
4702         }
4703         return ret;
4704         
4705     }
4706 });
4707
4708
4709 Roo.form.Action.Load = function(form, options){
4710     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4711     this.reader = this.form.reader;
4712 };
4713
4714 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4715     type : 'load',
4716
4717     run : function(){
4718         
4719         Roo.Ajax.request(Roo.apply(
4720                 this.createCallback(), {
4721                     method:this.getMethod(),
4722                     url:this.getUrl(false),
4723                     params:this.getParams()
4724         }));
4725     },
4726
4727     success : function(response){
4728         
4729         var result = this.processResponse(response);
4730         if(result === true || !result.success || !result.data){
4731             this.failureType = Roo.form.Action.LOAD_FAILURE;
4732             this.form.afterAction(this, false);
4733             return;
4734         }
4735         this.form.clearInvalid();
4736         this.form.setValues(result.data);
4737         this.form.afterAction(this, true);
4738     },
4739
4740     handleResponse : function(response){
4741         if(this.form.reader){
4742             var rs = this.form.reader.read(response);
4743             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4744             return {
4745                 success : rs.success,
4746                 data : data
4747             };
4748         }
4749         return Roo.decode(response.responseText);
4750     }
4751 });
4752
4753 Roo.form.Action.ACTION_TYPES = {
4754     'load' : Roo.form.Action.Load,
4755     'submit' : Roo.form.Action.Submit
4756 };/*
4757  * - LGPL
4758  *
4759  * form
4760  * 
4761  */
4762
4763 /**
4764  * @class Roo.bootstrap.Form
4765  * @extends Roo.bootstrap.Component
4766  * Bootstrap Form class
4767  * @cfg {String} method  GET | POST (default POST)
4768  * @cfg {String} labelAlign top | left (default top)
4769   * @cfg {String} align left  | right - for navbars
4770
4771  * 
4772  * @constructor
4773  * Create a new Form
4774  * @param {Object} config The config object
4775  */
4776
4777
4778 Roo.bootstrap.Form = function(config){
4779     Roo.bootstrap.Form.superclass.constructor.call(this, config);
4780     this.addEvents({
4781         /**
4782          * @event clientvalidation
4783          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4784          * @param {Form} this
4785          * @param {Boolean} valid true if the form has passed client-side validation
4786          */
4787         clientvalidation: true,
4788         /**
4789          * @event beforeaction
4790          * Fires before any action is performed. Return false to cancel the action.
4791          * @param {Form} this
4792          * @param {Action} action The action to be performed
4793          */
4794         beforeaction: true,
4795         /**
4796          * @event actionfailed
4797          * Fires when an action fails.
4798          * @param {Form} this
4799          * @param {Action} action The action that failed
4800          */
4801         actionfailed : true,
4802         /**
4803          * @event actioncomplete
4804          * Fires when an action is completed.
4805          * @param {Form} this
4806          * @param {Action} action The action that completed
4807          */
4808         actioncomplete : true
4809     });
4810     
4811 };
4812
4813 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
4814       
4815      /**
4816      * @cfg {String} method
4817      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4818      */
4819     method : 'POST',
4820     /**
4821      * @cfg {String} url
4822      * The URL to use for form actions if one isn't supplied in the action options.
4823      */
4824     /**
4825      * @cfg {Boolean} fileUpload
4826      * Set to true if this form is a file upload.
4827      */
4828      
4829     /**
4830      * @cfg {Object} baseParams
4831      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4832      */
4833       
4834     /**
4835      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4836      */
4837     timeout: 30,
4838     /**
4839      * @cfg {Sting} align (left|right) for navbar forms
4840      */
4841     align : 'left',
4842
4843     // private
4844     activeAction : null,
4845  
4846     /**
4847      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4848      * element by passing it or its id or mask the form itself by passing in true.
4849      * @type Mixed
4850      */
4851     waitMsgTarget : false,
4852     
4853      
4854     
4855     /**
4856      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4857      * element by passing it or its id or mask the form itself by passing in true.
4858      * @type Mixed
4859      */
4860     
4861     getAutoCreate : function(){
4862         
4863         var cfg = {
4864             tag: 'form',
4865             method : this.method || 'POST',
4866             id : this.id || Roo.id(),
4867             cls : ''
4868         }
4869         if (this.parent().xtype.match(/^Nav/)) {
4870             cfg.cls = 'navbar-form navbar-' + this.align;
4871             
4872         }
4873         
4874         if (this.labelAlign == 'left' ) {
4875             cfg.cls += ' form-horizontal';
4876         }
4877         
4878         
4879         return cfg;
4880     },
4881     initEvents : function()
4882     {
4883         this.el.on('submit', this.onSubmit, this);
4884         
4885         
4886     },
4887     // private
4888     onSubmit : function(e){
4889         e.stopEvent();
4890     },
4891     
4892      /**
4893      * Returns true if client-side validation on the form is successful.
4894      * @return Boolean
4895      */
4896     isValid : function(){
4897         var items = this.getItems();
4898         var valid = true;
4899         items.each(function(f){
4900            if(!f.validate()){
4901                valid = false;
4902                
4903            }
4904         });
4905         return valid;
4906     },
4907     /**
4908      * Returns true if any fields in this form have changed since their original load.
4909      * @return Boolean
4910      */
4911     isDirty : function(){
4912         var dirty = false;
4913         var items = this.getItems();
4914         items.each(function(f){
4915            if(f.isDirty()){
4916                dirty = true;
4917                return false;
4918            }
4919            return true;
4920         });
4921         return dirty;
4922     },
4923      /**
4924      * Performs a predefined action (submit or load) or custom actions you define on this form.
4925      * @param {String} actionName The name of the action type
4926      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
4927      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4928      * accept other config options):
4929      * <pre>
4930 Property          Type             Description
4931 ----------------  ---------------  ----------------------------------------------------------------------------------
4932 url               String           The url for the action (defaults to the form's url)
4933 method            String           The form method to use (defaults to the form's method, or POST if not defined)
4934 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
4935 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
4936                                    validate the form on the client (defaults to false)
4937      * </pre>
4938      * @return {BasicForm} this
4939      */
4940     doAction : function(action, options){
4941         if(typeof action == 'string'){
4942             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4943         }
4944         if(this.fireEvent('beforeaction', this, action) !== false){
4945             this.beforeAction(action);
4946             action.run.defer(100, action);
4947         }
4948         return this;
4949     },
4950     
4951     // private
4952     beforeAction : function(action){
4953         var o = action.options;
4954         
4955         // not really supported yet.. ??
4956         
4957         //if(this.waitMsgTarget === true){
4958             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4959         //}else if(this.waitMsgTarget){
4960         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4961         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4962         //}else {
4963         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4964        // }
4965          
4966     },
4967
4968     // private
4969     afterAction : function(action, success){
4970         this.activeAction = null;
4971         var o = action.options;
4972         
4973         //if(this.waitMsgTarget === true){
4974             this.el.unmask();
4975         //}else if(this.waitMsgTarget){
4976         //    this.waitMsgTarget.unmask();
4977         //}else{
4978         //    Roo.MessageBox.updateProgress(1);
4979         //    Roo.MessageBox.hide();
4980        // }
4981         // 
4982         if(success){
4983             if(o.reset){
4984                 this.reset();
4985             }
4986             Roo.callback(o.success, o.scope, [this, action]);
4987             this.fireEvent('actioncomplete', this, action);
4988             
4989         }else{
4990             
4991             // failure condition..
4992             // we have a scenario where updates need confirming.
4993             // eg. if a locking scenario exists..
4994             // we look for { errors : { needs_confirm : true }} in the response.
4995             if (
4996                 (typeof(action.result) != 'undefined')  &&
4997                 (typeof(action.result.errors) != 'undefined')  &&
4998                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4999            ){
5000                 var _t = this;
5001                 Roo.log("not supported yet");
5002                  /*
5003                 
5004                 Roo.MessageBox.confirm(
5005                     "Change requires confirmation",
5006                     action.result.errorMsg,
5007                     function(r) {
5008                         if (r != 'yes') {
5009                             return;
5010                         }
5011                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5012                     }
5013                     
5014                 );
5015                 */
5016                 
5017                 
5018                 return;
5019             }
5020             
5021             Roo.callback(o.failure, o.scope, [this, action]);
5022             // show an error message if no failed handler is set..
5023             if (!this.hasListener('actionfailed')) {
5024                 Roo.log("need to add dialog support");
5025                 /*
5026                 Roo.MessageBox.alert("Error",
5027                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5028                         action.result.errorMsg :
5029                         "Saving Failed, please check your entries or try again"
5030                 );
5031                 */
5032             }
5033             
5034             this.fireEvent('actionfailed', this, action);
5035         }
5036         
5037     },
5038     /**
5039      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5040      * @param {String} id The value to search for
5041      * @return Field
5042      */
5043     findField : function(id){
5044         var items = this.getItems();
5045         var field = items.get(id);
5046         if(!field){
5047              items.each(function(f){
5048                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5049                     field = f;
5050                     return false;
5051                 }
5052                 return true;
5053             });
5054         }
5055         return field || null;
5056     },
5057      /**
5058      * Mark fields in this form invalid in bulk.
5059      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5060      * @return {BasicForm} this
5061      */
5062     markInvalid : function(errors){
5063         if(errors instanceof Array){
5064             for(var i = 0, len = errors.length; i < len; i++){
5065                 var fieldError = errors[i];
5066                 var f = this.findField(fieldError.id);
5067                 if(f){
5068                     f.markInvalid(fieldError.msg);
5069                 }
5070             }
5071         }else{
5072             var field, id;
5073             for(id in errors){
5074                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5075                     field.markInvalid(errors[id]);
5076                 }
5077             }
5078         }
5079         //Roo.each(this.childForms || [], function (f) {
5080         //    f.markInvalid(errors);
5081         //});
5082         
5083         return this;
5084     },
5085
5086     /**
5087      * Set values for fields in this form in bulk.
5088      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5089      * @return {BasicForm} this
5090      */
5091     setValues : function(values){
5092         if(values instanceof Array){ // array of objects
5093             for(var i = 0, len = values.length; i < len; i++){
5094                 var v = values[i];
5095                 var f = this.findField(v.id);
5096                 if(f){
5097                     f.setValue(v.value);
5098                     if(this.trackResetOnLoad){
5099                         f.originalValue = f.getValue();
5100                     }
5101                 }
5102             }
5103         }else{ // object hash
5104             var field, id;
5105             for(id in values){
5106                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5107                     
5108                     if (field.setFromData && 
5109                         field.valueField && 
5110                         field.displayField &&
5111                         // combos' with local stores can 
5112                         // be queried via setValue()
5113                         // to set their value..
5114                         (field.store && !field.store.isLocal)
5115                         ) {
5116                         // it's a combo
5117                         var sd = { };
5118                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5119                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5120                         field.setFromData(sd);
5121                         
5122                     } else {
5123                         field.setValue(values[id]);
5124                     }
5125                     
5126                     
5127                     if(this.trackResetOnLoad){
5128                         field.originalValue = field.getValue();
5129                     }
5130                 }
5131             }
5132         }
5133          
5134         //Roo.each(this.childForms || [], function (f) {
5135         //    f.setValues(values);
5136         //});
5137                 
5138         return this;
5139     },
5140
5141     /**
5142      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5143      * they are returned as an array.
5144      * @param {Boolean} asString
5145      * @return {Object}
5146      */
5147     getValues : function(asString){
5148         //if (this.childForms) {
5149             // copy values from the child forms
5150         //    Roo.each(this.childForms, function (f) {
5151         //        this.setValues(f.getValues());
5152         //    }, this);
5153         //}
5154         
5155         
5156         
5157         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5158         if(asString === true){
5159             return fs;
5160         }
5161         return Roo.urlDecode(fs);
5162     },
5163     
5164     /**
5165      * Returns the fields in this form as an object with key/value pairs. 
5166      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5167      * @return {Object}
5168      */
5169     getFieldValues : function(with_hidden)
5170     {
5171         var items = this.getItems();
5172         var ret = {};
5173         items.each(function(f){
5174             if (!f.getName()) {
5175                 return;
5176             }
5177             var v = f.getValue();
5178             if (f.inputType =='radio') {
5179                 if (typeof(ret[f.getName()]) == 'undefined') {
5180                     ret[f.getName()] = ''; // empty..
5181                 }
5182                 
5183                 if (!f.el.dom.checked) {
5184                     return;
5185                     
5186                 }
5187                 v = f.el.dom.value;
5188                 
5189             }
5190             
5191             // not sure if this supported any more..
5192             if ((typeof(v) == 'object') && f.getRawValue) {
5193                 v = f.getRawValue() ; // dates..
5194             }
5195             // combo boxes where name != hiddenName...
5196             if (f.name != f.getName()) {
5197                 ret[f.name] = f.getRawValue();
5198             }
5199             ret[f.getName()] = v;
5200         });
5201         
5202         return ret;
5203     },
5204
5205     /**
5206      * Clears all invalid messages in this form.
5207      * @return {BasicForm} this
5208      */
5209     clearInvalid : function(){
5210         var items = this.getItems();
5211         
5212         items.each(function(f){
5213            f.clearInvalid();
5214         });
5215         
5216         
5217         
5218         return this;
5219     },
5220
5221     /**
5222      * Resets this form.
5223      * @return {BasicForm} this
5224      */
5225     reset : function(){
5226         var items = this.getItems();
5227         items.each(function(f){
5228             f.reset();
5229         });
5230         
5231         Roo.each(this.childForms || [], function (f) {
5232             f.reset();
5233         });
5234        
5235         
5236         return this;
5237     },
5238     getItems : function()
5239     {
5240         var r=new Roo.util.MixedCollection(false, function(o){
5241             return o.id || (o.id = Roo.id());
5242         });
5243         var iter = function(el) {
5244             if (el.inputEl) {
5245                 r.add(el);
5246             }
5247             if (!el.items) {
5248                 return;
5249             }
5250             Roo.each(el.items,function(e) {
5251                 iter(e);
5252             });
5253             
5254             
5255         };
5256         iter(this);
5257         return r;
5258         
5259         
5260         
5261         
5262     }
5263     
5264 });
5265
5266  
5267 /*
5268  * Based on:
5269  * Ext JS Library 1.1.1
5270  * Copyright(c) 2006-2007, Ext JS, LLC.
5271  *
5272  * Originally Released Under LGPL - original licence link has changed is not relivant.
5273  *
5274  * Fork - LGPL
5275  * <script type="text/javascript">
5276  */
5277 /**
5278  * @class Roo.form.VTypes
5279  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5280  * @singleton
5281  */
5282 Roo.form.VTypes = function(){
5283     // closure these in so they are only created once.
5284     var alpha = /^[a-zA-Z_]+$/;
5285     var alphanum = /^[a-zA-Z0-9_]+$/;
5286     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5287     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5288
5289     // All these messages and functions are configurable
5290     return {
5291         /**
5292          * The function used to validate email addresses
5293          * @param {String} value The email address
5294          */
5295         'email' : function(v){
5296             return email.test(v);
5297         },
5298         /**
5299          * The error text to display when the email validation function returns false
5300          * @type String
5301          */
5302         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5303         /**
5304          * The keystroke filter mask to be applied on email input
5305          * @type RegExp
5306          */
5307         'emailMask' : /[a-z0-9_\.\-@]/i,
5308
5309         /**
5310          * The function used to validate URLs
5311          * @param {String} value The URL
5312          */
5313         'url' : function(v){
5314             return url.test(v);
5315         },
5316         /**
5317          * The error text to display when the url validation function returns false
5318          * @type String
5319          */
5320         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5321         
5322         /**
5323          * The function used to validate alpha values
5324          * @param {String} value The value
5325          */
5326         'alpha' : function(v){
5327             return alpha.test(v);
5328         },
5329         /**
5330          * The error text to display when the alpha validation function returns false
5331          * @type String
5332          */
5333         'alphaText' : 'This field should only contain letters and _',
5334         /**
5335          * The keystroke filter mask to be applied on alpha input
5336          * @type RegExp
5337          */
5338         'alphaMask' : /[a-z_]/i,
5339
5340         /**
5341          * The function used to validate alphanumeric values
5342          * @param {String} value The value
5343          */
5344         'alphanum' : function(v){
5345             return alphanum.test(v);
5346         },
5347         /**
5348          * The error text to display when the alphanumeric validation function returns false
5349          * @type String
5350          */
5351         'alphanumText' : 'This field should only contain letters, numbers and _',
5352         /**
5353          * The keystroke filter mask to be applied on alphanumeric input
5354          * @type RegExp
5355          */
5356         'alphanumMask' : /[a-z0-9_]/i
5357     };
5358 }();/*
5359  * - LGPL
5360  *
5361  * Input
5362  * 
5363  */
5364
5365 /**
5366  * @class Roo.bootstrap.Input
5367  * @extends Roo.bootstrap.Component
5368  * Bootstrap Input class
5369  * @cfg {Boolean} disabled is it disabled
5370  * @cfg {String} fieldLabel - the label associated
5371  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5372  * @cfg {String} name name of the input
5373  * @cfg {string} fieldLabel - the label associated
5374  * @cfg {string}  inputType - input / file submit ...
5375  * @cfg {string} placeholder - placeholder to put in text.
5376  * @cfg {string}  before - input group add on before
5377  * @cfg {string} after - input group add on after
5378  * @cfg {string} size - (lg|sm) or leave empty..
5379  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5380  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5381  * @cfg {Number} md colspan out of 12 for computer-sized screens
5382  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5383  * @cfg {string} value default value of the input
5384  * @cfg {Number} labelWidth set the width of label (0-12)
5385  * @cfg {String} labelAlign (top|left)
5386  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Input
5391  * @param {Object} config The config object
5392  */
5393
5394 Roo.bootstrap.Input = function(config){
5395     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5396    
5397         this.addEvents({
5398             /**
5399              * @event focus
5400              * Fires when this field receives input focus.
5401              * @param {Roo.form.Field} this
5402              */
5403             focus : true,
5404             /**
5405              * @event blur
5406              * Fires when this field loses input focus.
5407              * @param {Roo.form.Field} this
5408              */
5409             blur : true,
5410             /**
5411              * @event specialkey
5412              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5413              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5414              * @param {Roo.form.Field} this
5415              * @param {Roo.EventObject} e The event object
5416              */
5417             specialkey : true,
5418             /**
5419              * @event change
5420              * Fires just before the field blurs if the field value has changed.
5421              * @param {Roo.form.Field} this
5422              * @param {Mixed} newValue The new value
5423              * @param {Mixed} oldValue The original value
5424              */
5425             change : true,
5426             /**
5427              * @event invalid
5428              * Fires after the field has been marked as invalid.
5429              * @param {Roo.form.Field} this
5430              * @param {String} msg The validation message
5431              */
5432             invalid : true,
5433             /**
5434              * @event valid
5435              * Fires after the field has been validated with no errors.
5436              * @param {Roo.form.Field} this
5437              */
5438             valid : true,
5439              /**
5440              * @event keyup
5441              * Fires after the key up
5442              * @param {Roo.form.Field} this
5443              * @param {Roo.EventObject}  e The event Object
5444              */
5445             keyup : true
5446         });
5447 };
5448
5449 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5450      /**
5451      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5452       automatic validation (defaults to "keyup").
5453      */
5454     validationEvent : "keyup",
5455      /**
5456      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5457      */
5458     validateOnBlur : true,
5459     /**
5460      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5461      */
5462     validationDelay : 250,
5463      /**
5464      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5465      */
5466     focusClass : "x-form-focus",  // not needed???
5467     
5468        
5469     /**
5470      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5471      */
5472     invalidClass : "has-error",
5473     
5474     /**
5475      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5476      */
5477     selectOnFocus : false,
5478     
5479      /**
5480      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5481      */
5482     maskRe : null,
5483        /**
5484      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5485      */
5486     vtype : null,
5487     
5488       /**
5489      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5490      */
5491     disableKeyFilter : false,
5492     
5493        /**
5494      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5495      */
5496     disabled : false,
5497      /**
5498      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5499      */
5500     allowBlank : true,
5501     /**
5502      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5503      */
5504     blankText : "This field is required",
5505     
5506      /**
5507      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5508      */
5509     minLength : 0,
5510     /**
5511      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5512      */
5513     maxLength : Number.MAX_VALUE,
5514     /**
5515      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5516      */
5517     minLengthText : "The minimum length for this field is {0}",
5518     /**
5519      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5520      */
5521     maxLengthText : "The maximum length for this field is {0}",
5522   
5523     
5524     /**
5525      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5526      * If available, this function will be called only after the basic validators all return true, and will be passed the
5527      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5528      */
5529     validator : null,
5530     /**
5531      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5532      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5533      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5534      */
5535     regex : null,
5536     /**
5537      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5538      */
5539     regexText : "",
5540     
5541     
5542     
5543     fieldLabel : '',
5544     inputType : 'text',
5545     
5546     name : false,
5547     placeholder: false,
5548     before : false,
5549     after : false,
5550     size : false,
5551     // private
5552     hasFocus : false,
5553     preventMark: false,
5554     isFormField : true,
5555     value : '',
5556     labelWidth : 2,
5557     labelAlign : false,
5558     readOnly : false,
5559     
5560     parentLabelAlign : function()
5561     {
5562         var parent = this;
5563         while (parent.parent()) {
5564             parent = parent.parent();
5565             if (typeof(parent.labelAlign) !='undefined') {
5566                 return parent.labelAlign;
5567             }
5568         }
5569         return 'left';
5570         
5571     },
5572     
5573     getAutoCreate : function(){
5574         
5575         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5576         
5577         var id = Roo.id();
5578         
5579         var cfg = {};
5580         
5581         if(this.inputType != 'hidden'){
5582             cfg.cls = 'form-group' //input-group
5583         }
5584         
5585         var input =  {
5586             tag: 'input',
5587             id : id,
5588             type : this.inputType,
5589             value : this.value,
5590             cls : 'form-control',
5591             placeholder : this.placeholder || ''
5592             
5593         };
5594         
5595         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5596             input.maxLength = this.maxLength;
5597         }
5598         
5599         if (this.disabled) {
5600             input.disabled=true;
5601         }
5602         
5603         if (this.readOnly) {
5604             input.readonly=true;
5605         }
5606         
5607         if (this.name) {
5608             input.name = this.name;
5609         }
5610         if (this.size) {
5611             input.cls += ' input-' + this.size;
5612         }
5613         var settings=this;
5614         ['xs','sm','md','lg'].map(function(size){
5615             if (settings[size]) {
5616                 cfg.cls += ' col-' + size + '-' + settings[size];
5617             }
5618         });
5619         
5620         var inputblock = input;
5621         
5622         if (this.before || this.after) {
5623             
5624             inputblock = {
5625                 cls : 'input-group',
5626                 cn :  [] 
5627             };
5628             if (this.before) {
5629                 inputblock.cn.push({
5630                     tag :'span',
5631                     cls : 'input-group-addon',
5632                     html : this.before
5633                 });
5634             }
5635             inputblock.cn.push(input);
5636             if (this.after) {
5637                 inputblock.cn.push({
5638                     tag :'span',
5639                     cls : 'input-group-addon',
5640                     html : this.after
5641                 });
5642             }
5643             
5644         };
5645         
5646         if (align ==='left' && this.fieldLabel.length) {
5647                 Roo.log("left and has label");
5648                 cfg.cn = [
5649                     
5650                     {
5651                         tag: 'label',
5652                         'for' :  id,
5653                         cls : 'control-label col-sm-' + this.labelWidth,
5654                         html : this.fieldLabel
5655                         
5656                     },
5657                     {
5658                         cls : "col-sm-" + (12 - this.labelWidth), 
5659                         cn: [
5660                             inputblock
5661                         ]
5662                     }
5663                     
5664                 ];
5665         } else if ( this.fieldLabel.length) {
5666                 Roo.log(" label");
5667                  cfg.cn = [
5668                    
5669                     {
5670                         tag: 'label',
5671                         //cls : 'input-group-addon',
5672                         html : this.fieldLabel
5673                         
5674                     },
5675                     
5676                     inputblock
5677                     
5678                 ];
5679
5680         } else {
5681             
5682                 Roo.log(" no label && no align");
5683                 cfg.cn = [
5684                     
5685                         inputblock
5686                     
5687                 ];
5688                 
5689                 
5690         };
5691         Roo.log('input-parentType: ' + this.parentType);
5692         
5693         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5694            cfg.cls += ' navbar-form';
5695            Roo.log(cfg);
5696         }
5697         
5698         return cfg;
5699         
5700     },
5701     /**
5702      * return the real input element.
5703      */
5704     inputEl: function ()
5705     {
5706         return this.el.select('input.form-control',true).first();
5707     },
5708     setDisabled : function(v)
5709     {
5710         var i  = this.inputEl().dom;
5711         if (!v) {
5712             i.removeAttribute('disabled');
5713             return;
5714             
5715         }
5716         i.setAttribute('disabled','true');
5717     },
5718     initEvents : function()
5719     {
5720         
5721         this.inputEl().on("keydown" , this.fireKey,  this);
5722         this.inputEl().on("focus", this.onFocus,  this);
5723         this.inputEl().on("blur", this.onBlur,  this);
5724         
5725         this.inputEl().relayEvent('keyup', this);
5726
5727         // reference to original value for reset
5728         this.originalValue = this.getValue();
5729         //Roo.form.TextField.superclass.initEvents.call(this);
5730         if(this.validationEvent == 'keyup'){
5731             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5732             this.inputEl().on('keyup', this.filterValidation, this);
5733         }
5734         else if(this.validationEvent !== false){
5735             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5736         }
5737         
5738         if(this.selectOnFocus){
5739             this.on("focus", this.preFocus, this);
5740             
5741         }
5742         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5743             this.inputEl().on("keypress", this.filterKeys, this);
5744         }
5745        /* if(this.grow){
5746             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
5747             this.el.on("click", this.autoSize,  this);
5748         }
5749         */
5750         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5751             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5752         }
5753         
5754     },
5755     filterValidation : function(e){
5756         if(!e.isNavKeyPress()){
5757             this.validationTask.delay(this.validationDelay);
5758         }
5759     },
5760      /**
5761      * Validates the field value
5762      * @return {Boolean} True if the value is valid, else false
5763      */
5764     validate : function(){
5765         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5766         if(this.disabled || this.validateValue(this.getRawValue())){
5767             this.clearInvalid();
5768             return true;
5769         }
5770         return false;
5771     },
5772     
5773     
5774     /**
5775      * Validates a value according to the field's validation rules and marks the field as invalid
5776      * if the validation fails
5777      * @param {Mixed} value The value to validate
5778      * @return {Boolean} True if the value is valid, else false
5779      */
5780     validateValue : function(value){
5781         if(value.length < 1)  { // if it's blank
5782              if(this.allowBlank){
5783                 this.clearInvalid();
5784                 return true;
5785              }else{
5786                 this.markInvalid(this.blankText);
5787                 return false;
5788              }
5789         }
5790         if(value.length < this.minLength){
5791             this.markInvalid(String.format(this.minLengthText, this.minLength));
5792             return false;
5793         }
5794         if(value.length > this.maxLength){
5795             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5796             return false;
5797         }
5798         if(this.vtype){
5799             var vt = Roo.form.VTypes;
5800             if(!vt[this.vtype](value, this)){
5801                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5802                 return false;
5803             }
5804         }
5805         if(typeof this.validator == "function"){
5806             var msg = this.validator(value);
5807             if(msg !== true){
5808                 this.markInvalid(msg);
5809                 return false;
5810             }
5811         }
5812         if(this.regex && !this.regex.test(value)){
5813             this.markInvalid(this.regexText);
5814             return false;
5815         }
5816         return true;
5817     },
5818
5819     
5820     
5821      // private
5822     fireKey : function(e){
5823         //Roo.log('field ' + e.getKey());
5824         if(e.isNavKeyPress()){
5825             this.fireEvent("specialkey", this, e);
5826         }
5827     },
5828     focus : function (selectText){
5829         if(this.rendered){
5830             this.inputEl().focus();
5831             if(selectText === true){
5832                 this.inputEl().dom.select();
5833             }
5834         }
5835         return this;
5836     } ,
5837     
5838     onFocus : function(){
5839         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5840            // this.el.addClass(this.focusClass);
5841         }
5842         if(!this.hasFocus){
5843             this.hasFocus = true;
5844             this.startValue = this.getValue();
5845             this.fireEvent("focus", this);
5846         }
5847     },
5848     
5849     beforeBlur : Roo.emptyFn,
5850
5851     
5852     // private
5853     onBlur : function(){
5854         this.beforeBlur();
5855         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5856             //this.el.removeClass(this.focusClass);
5857         }
5858         this.hasFocus = false;
5859         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5860             this.validate();
5861         }
5862         var v = this.getValue();
5863         if(String(v) !== String(this.startValue)){
5864             this.fireEvent('change', this, v, this.startValue);
5865         }
5866         this.fireEvent("blur", this);
5867     },
5868     
5869     /**
5870      * Resets the current field value to the originally loaded value and clears any validation messages
5871      */
5872     reset : function(){
5873         this.setValue(this.originalValue);
5874         this.clearInvalid();
5875     },
5876      /**
5877      * Returns the name of the field
5878      * @return {Mixed} name The name field
5879      */
5880     getName: function(){
5881         return this.name;
5882     },
5883      /**
5884      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
5885      * @return {Mixed} value The field value
5886      */
5887     getValue : function(){
5888         return this.inputEl().getValue();
5889     },
5890     /**
5891      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
5892      * @return {Mixed} value The field value
5893      */
5894     getRawValue : function(){
5895         var v = this.inputEl().getValue();
5896         
5897         return v;
5898     },
5899     
5900     /**
5901      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
5902      * @param {Mixed} value The value to set
5903      */
5904     setRawValue : function(v){
5905         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5906     },
5907     
5908     selectText : function(start, end){
5909         var v = this.getRawValue();
5910         if(v.length > 0){
5911             start = start === undefined ? 0 : start;
5912             end = end === undefined ? v.length : end;
5913             var d = this.inputEl().dom;
5914             if(d.setSelectionRange){
5915                 d.setSelectionRange(start, end);
5916             }else if(d.createTextRange){
5917                 var range = d.createTextRange();
5918                 range.moveStart("character", start);
5919                 range.moveEnd("character", v.length-end);
5920                 range.select();
5921             }
5922         }
5923     },
5924     
5925     /**
5926      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
5927      * @param {Mixed} value The value to set
5928      */
5929     setValue : function(v){
5930         this.value = v;
5931         if(this.rendered){
5932             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5933             this.validate();
5934         }
5935     },
5936     
5937     /*
5938     processValue : function(value){
5939         if(this.stripCharsRe){
5940             var newValue = value.replace(this.stripCharsRe, '');
5941             if(newValue !== value){
5942                 this.setRawValue(newValue);
5943                 return newValue;
5944             }
5945         }
5946         return value;
5947     },
5948   */
5949     preFocus : function(){
5950         
5951         if(this.selectOnFocus){
5952             this.inputEl().dom.select();
5953         }
5954     },
5955     filterKeys : function(e){
5956         var k = e.getKey();
5957         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5958             return;
5959         }
5960         var c = e.getCharCode(), cc = String.fromCharCode(c);
5961         if(Roo.isIE && (e.isSpecialKey() || !cc)){
5962             return;
5963         }
5964         if(!this.maskRe.test(cc)){
5965             e.stopEvent();
5966         }
5967     },
5968      /**
5969      * Clear any invalid styles/messages for this field
5970      */
5971     clearInvalid : function(){
5972         
5973         if(!this.el || this.preventMark){ // not rendered
5974             return;
5975         }
5976         this.el.removeClass(this.invalidClass);
5977         /*
5978         switch(this.msgTarget){
5979             case 'qtip':
5980                 this.el.dom.qtip = '';
5981                 break;
5982             case 'title':
5983                 this.el.dom.title = '';
5984                 break;
5985             case 'under':
5986                 if(this.errorEl){
5987                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5988                 }
5989                 break;
5990             case 'side':
5991                 if(this.errorIcon){
5992                     this.errorIcon.dom.qtip = '';
5993                     this.errorIcon.hide();
5994                     this.un('resize', this.alignErrorIcon, this);
5995                 }
5996                 break;
5997             default:
5998                 var t = Roo.getDom(this.msgTarget);
5999                 t.innerHTML = '';
6000                 t.style.display = 'none';
6001                 break;
6002         }
6003         */
6004         this.fireEvent('valid', this);
6005     },
6006      /**
6007      * Mark this field as invalid
6008      * @param {String} msg The validation message
6009      */
6010     markInvalid : function(msg){
6011         if(!this.el  || this.preventMark){ // not rendered
6012             return;
6013         }
6014         this.el.addClass(this.invalidClass);
6015         /*
6016         msg = msg || this.invalidText;
6017         switch(this.msgTarget){
6018             case 'qtip':
6019                 this.el.dom.qtip = msg;
6020                 this.el.dom.qclass = 'x-form-invalid-tip';
6021                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6022                     Roo.QuickTips.enable();
6023                 }
6024                 break;
6025             case 'title':
6026                 this.el.dom.title = msg;
6027                 break;
6028             case 'under':
6029                 if(!this.errorEl){
6030                     var elp = this.el.findParent('.x-form-element', 5, true);
6031                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6032                     this.errorEl.setWidth(elp.getWidth(true)-20);
6033                 }
6034                 this.errorEl.update(msg);
6035                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6036                 break;
6037             case 'side':
6038                 if(!this.errorIcon){
6039                     var elp = this.el.findParent('.x-form-element', 5, true);
6040                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6041                 }
6042                 this.alignErrorIcon();
6043                 this.errorIcon.dom.qtip = msg;
6044                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6045                 this.errorIcon.show();
6046                 this.on('resize', this.alignErrorIcon, this);
6047                 break;
6048             default:
6049                 var t = Roo.getDom(this.msgTarget);
6050                 t.innerHTML = msg;
6051                 t.style.display = this.msgDisplay;
6052                 break;
6053         }
6054         */
6055         this.fireEvent('invalid', this, msg);
6056     },
6057     // private
6058     SafariOnKeyDown : function(event)
6059     {
6060         // this is a workaround for a password hang bug on chrome/ webkit.
6061         
6062         var isSelectAll = false;
6063         
6064         if(this.inputEl().dom.selectionEnd > 0){
6065             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6066         }
6067         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6068             event.preventDefault();
6069             this.setValue('');
6070             return;
6071         }
6072         
6073         if(isSelectAll){ // backspace and delete key
6074             
6075             event.preventDefault();
6076             // this is very hacky as keydown always get's upper case.
6077             //
6078             var cc = String.fromCharCode(event.getCharCode());
6079             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6080             
6081         }
6082     },
6083     adjustWidth : function(tag, w){
6084         tag = tag.toLowerCase();
6085         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6086             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6087                 if(tag == 'input'){
6088                     return w + 2;
6089                 }
6090                 if(tag == 'textarea'){
6091                     return w-2;
6092                 }
6093             }else if(Roo.isOpera){
6094                 if(tag == 'input'){
6095                     return w + 2;
6096                 }
6097                 if(tag == 'textarea'){
6098                     return w-2;
6099                 }
6100             }
6101         }
6102         return w;
6103     }
6104     
6105 });
6106
6107  
6108 /*
6109  * - LGPL
6110  *
6111  * Input
6112  * 
6113  */
6114
6115 /**
6116  * @class Roo.bootstrap.TextArea
6117  * @extends Roo.bootstrap.Input
6118  * Bootstrap TextArea class
6119  * @cfg {Number} cols Specifies the visible width of a text area
6120  * @cfg {Number} rows Specifies the visible number of lines in a text area
6121  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6122  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6123  * @cfg {string} html text
6124  * 
6125  * @constructor
6126  * Create a new TextArea
6127  * @param {Object} config The config object
6128  */
6129
6130 Roo.bootstrap.TextArea = function(config){
6131     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6132    
6133 };
6134
6135 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6136      
6137     cols : false,
6138     rows : 5,
6139     readOnly : false,
6140     warp : 'soft',
6141     resize : false,
6142     value: false,
6143     html: false,
6144     
6145     getAutoCreate : function(){
6146         
6147         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6148         
6149         var id = Roo.id();
6150         
6151         var cfg = {};
6152         
6153         var input =  {
6154             tag: 'textarea',
6155             id : id,
6156             warp : this.warp,
6157             rows : this.rows,
6158             value : this.value || '',
6159             html: this.html || '',
6160             cls : 'form-control',
6161             placeholder : this.placeholder || '' 
6162             
6163         };
6164         
6165         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6166             input.maxLength = this.maxLength;
6167         }
6168         
6169         if(this.resize){
6170             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6171         }
6172         
6173         if(this.cols){
6174             input.cols = this.cols;
6175         }
6176         
6177         if (this.readOnly) {
6178             input.readonly = true;
6179         }
6180         
6181         if (this.name) {
6182             input.name = this.name;
6183         }
6184         
6185         if (this.size) {
6186             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6187         }
6188         
6189         var settings=this;
6190         ['xs','sm','md','lg'].map(function(size){
6191             if (settings[size]) {
6192                 cfg.cls += ' col-' + size + '-' + settings[size];
6193             }
6194         });
6195         
6196         var inputblock = input;
6197         
6198         if (this.before || this.after) {
6199             
6200             inputblock = {
6201                 cls : 'input-group',
6202                 cn :  [] 
6203             };
6204             if (this.before) {
6205                 inputblock.cn.push({
6206                     tag :'span',
6207                     cls : 'input-group-addon',
6208                     html : this.before
6209                 });
6210             }
6211             inputblock.cn.push(input);
6212             if (this.after) {
6213                 inputblock.cn.push({
6214                     tag :'span',
6215                     cls : 'input-group-addon',
6216                     html : this.after
6217                 });
6218             }
6219             
6220         }
6221         
6222         if (align ==='left' && this.fieldLabel.length) {
6223                 Roo.log("left and has label");
6224                 cfg.cn = [
6225                     
6226                     {
6227                         tag: 'label',
6228                         'for' :  id,
6229                         cls : 'control-label col-sm-' + this.labelWidth,
6230                         html : this.fieldLabel
6231                         
6232                     },
6233                     {
6234                         cls : "col-sm-" + (12 - this.labelWidth), 
6235                         cn: [
6236                             inputblock
6237                         ]
6238                     }
6239                     
6240                 ];
6241         } else if ( this.fieldLabel.length) {
6242                 Roo.log(" label");
6243                  cfg.cn = [
6244                    
6245                     {
6246                         tag: 'label',
6247                         //cls : 'input-group-addon',
6248                         html : this.fieldLabel
6249                         
6250                     },
6251                     
6252                     inputblock
6253                     
6254                 ];
6255
6256         } else {
6257             
6258                    Roo.log(" no label && no align");
6259                 cfg.cn = [
6260                     
6261                         inputblock
6262                     
6263                 ];
6264                 
6265                 
6266         }
6267         
6268         if (this.disabled) {
6269             input.disabled=true;
6270         }
6271         
6272         return cfg;
6273         
6274     },
6275     /**
6276      * return the real textarea element.
6277      */
6278     inputEl: function ()
6279     {
6280         return this.el.select('textarea.form-control',true).first();
6281     }
6282 });
6283
6284  
6285 /*
6286  * - LGPL
6287  *
6288  * trigger field - base class for combo..
6289  * 
6290  */
6291  
6292 /**
6293  * @class Roo.bootstrap.TriggerField
6294  * @extends Roo.bootstrap.Input
6295  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6296  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6297  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6298  * for which you can provide a custom implementation.  For example:
6299  * <pre><code>
6300 var trigger = new Roo.bootstrap.TriggerField();
6301 trigger.onTriggerClick = myTriggerFn;
6302 trigger.applyTo('my-field');
6303 </code></pre>
6304  *
6305  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6306  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6307  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6308  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6309  * @constructor
6310  * Create a new TriggerField.
6311  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6312  * to the base TextField)
6313  */
6314 Roo.bootstrap.TriggerField = function(config){
6315     this.mimicing = false;
6316     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6317 };
6318
6319 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6320     /**
6321      * @cfg {String} triggerClass A CSS class to apply to the trigger
6322      */
6323      /**
6324      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6325      */
6326     hideTrigger:false,
6327
6328     /** @cfg {Boolean} grow @hide */
6329     /** @cfg {Number} growMin @hide */
6330     /** @cfg {Number} growMax @hide */
6331
6332     /**
6333      * @hide 
6334      * @method
6335      */
6336     autoSize: Roo.emptyFn,
6337     // private
6338     monitorTab : true,
6339     // private
6340     deferHeight : true,
6341
6342     
6343     actionMode : 'wrap',
6344     
6345     
6346     
6347     getAutoCreate : function(){
6348        
6349         var parent = this.parent();
6350         
6351         var align = this.parentLabelAlign();
6352         
6353         var id = Roo.id();
6354         
6355         var cfg = {
6356             cls: 'form-group' //input-group
6357         };
6358         
6359         
6360         var input =  {
6361             tag: 'input',
6362             id : id,
6363             type : this.inputType,
6364             cls : 'form-control',
6365             autocomplete: 'off',
6366             placeholder : this.placeholder || '' 
6367             
6368         };
6369         if (this.name) {
6370             input.name = this.name;
6371         }
6372         if (this.size) {
6373             input.cls += ' input-' + this.size;
6374         }
6375         
6376         if (this.disabled) {
6377             input.disabled=true;
6378         }
6379         
6380         var inputblock = input;
6381         
6382         if (this.before || this.after) {
6383             
6384             inputblock = {
6385                 cls : 'input-group',
6386                 cn :  [] 
6387             };
6388             if (this.before) {
6389                 inputblock.cn.push({
6390                     tag :'span',
6391                     cls : 'input-group-addon',
6392                     html : this.before
6393                 });
6394             }
6395             inputblock.cn.push(input);
6396             if (this.after) {
6397                 inputblock.cn.push({
6398                     tag :'span',
6399                     cls : 'input-group-addon',
6400                     html : this.after
6401                 });
6402             }
6403             
6404         };
6405         
6406         var box = {
6407             tag: 'div',
6408             cn: [
6409                 {
6410                     tag: 'input',
6411                     type : 'hidden',
6412                     cls: 'form-hidden-field'
6413                 },
6414                 inputblock
6415             ]
6416             
6417         };
6418         
6419         if(this.multiple){
6420             Roo.log('multiple');
6421             
6422             box = {
6423                 tag: 'div',
6424                 cn: [
6425                     {
6426                         tag: 'input',
6427                         type : 'hidden',
6428                         cls: 'form-hidden-field'
6429                     },
6430                     {
6431                         tag: 'ul',
6432                         cls: 'select2-choices',
6433                         cn:[
6434                             {
6435                                 tag: 'li',
6436                                 cls: 'select2-search-field',
6437                                 cn: [
6438
6439                                     inputblock
6440                                 ]
6441                             }
6442                         ]
6443                     }
6444                 ]
6445             }
6446         };
6447         
6448         var combobox = {
6449             cls: 'select2-container input-group',
6450             cn: [
6451                 box,
6452                 {
6453                     tag: 'ul',
6454                     cls: 'typeahead typeahead-long dropdown-menu',
6455                     style: 'display:none'
6456                 }
6457             ]
6458         };
6459         
6460         if(!this.multiple){
6461             combobox.cn.push({
6462                 tag :'span',
6463                 cls : 'input-group-addon btn dropdown-toggle',
6464                 cn : [
6465                     {
6466                         tag: 'span',
6467                         cls: 'caret'
6468                     },
6469                     {
6470                         tag: 'span',
6471                         cls: 'combobox-clear',
6472                         cn  : [
6473                             {
6474                                 tag : 'i',
6475                                 cls: 'icon-remove'
6476                             }
6477                         ]
6478                     }
6479                 ]
6480
6481             })
6482         }
6483         
6484         if(this.multiple){
6485             combobox.cls += ' select2-container-multi';
6486         }
6487         
6488         if (align ==='left' && this.fieldLabel.length) {
6489             
6490                 Roo.log("left and has label");
6491                 cfg.cn = [
6492                     
6493                     {
6494                         tag: 'label',
6495                         'for' :  id,
6496                         cls : 'control-label col-sm-' + this.labelWidth,
6497                         html : this.fieldLabel
6498                         
6499                     },
6500                     {
6501                         cls : "col-sm-" + (12 - this.labelWidth), 
6502                         cn: [
6503                             combobox
6504                         ]
6505                     }
6506                     
6507                 ];
6508         } else if ( this.fieldLabel.length) {
6509                 Roo.log(" label");
6510                  cfg.cn = [
6511                    
6512                     {
6513                         tag: 'label',
6514                         //cls : 'input-group-addon',
6515                         html : this.fieldLabel
6516                         
6517                     },
6518                     
6519                     combobox
6520                     
6521                 ];
6522
6523         } else {
6524             
6525                 Roo.log(" no label && no align");
6526                 cfg = combobox
6527                      
6528                 
6529         }
6530          
6531         var settings=this;
6532         ['xs','sm','md','lg'].map(function(size){
6533             if (settings[size]) {
6534                 cfg.cls += ' col-' + size + '-' + settings[size];
6535             }
6536         });
6537         
6538         return cfg;
6539         
6540     },
6541     
6542     
6543     
6544     // private
6545     onResize : function(w, h){
6546 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6547 //        if(typeof w == 'number'){
6548 //            var x = w - this.trigger.getWidth();
6549 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6550 //            this.trigger.setStyle('left', x+'px');
6551 //        }
6552     },
6553
6554     // private
6555     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6556
6557     // private
6558     getResizeEl : function(){
6559         return this.inputEl();
6560     },
6561
6562     // private
6563     getPositionEl : function(){
6564         return this.inputEl();
6565     },
6566
6567     // private
6568     alignErrorIcon : function(){
6569         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6570     },
6571
6572     // private
6573     initEvents : function(){
6574         
6575         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6576         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6577         if(!this.multiple){
6578             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6579             if(this.hideTrigger){
6580                 this.trigger.setDisplayed(false);
6581             }
6582             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6583         }
6584         
6585         if(this.multiple){
6586             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6587         }
6588         
6589         //this.trigger.addClassOnOver('x-form-trigger-over');
6590         //this.trigger.addClassOnClick('x-form-trigger-click');
6591         
6592         //if(!this.width){
6593         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6594         //}
6595     },
6596
6597     // private
6598     initTrigger : function(){
6599        
6600     },
6601
6602     // private
6603     onDestroy : function(){
6604         if(this.trigger){
6605             this.trigger.removeAllListeners();
6606           //  this.trigger.remove();
6607         }
6608         //if(this.wrap){
6609         //    this.wrap.remove();
6610         //}
6611         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6612     },
6613
6614     // private
6615     onFocus : function(){
6616         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6617         /*
6618         if(!this.mimicing){
6619             this.wrap.addClass('x-trigger-wrap-focus');
6620             this.mimicing = true;
6621             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6622             if(this.monitorTab){
6623                 this.el.on("keydown", this.checkTab, this);
6624             }
6625         }
6626         */
6627     },
6628
6629     // private
6630     checkTab : function(e){
6631         if(e.getKey() == e.TAB){
6632             this.triggerBlur();
6633         }
6634     },
6635
6636     // private
6637     onBlur : function(){
6638         // do nothing
6639     },
6640
6641     // private
6642     mimicBlur : function(e, t){
6643         /*
6644         if(!this.wrap.contains(t) && this.validateBlur()){
6645             this.triggerBlur();
6646         }
6647         */
6648     },
6649
6650     // private
6651     triggerBlur : function(){
6652         this.mimicing = false;
6653         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6654         if(this.monitorTab){
6655             this.el.un("keydown", this.checkTab, this);
6656         }
6657         //this.wrap.removeClass('x-trigger-wrap-focus');
6658         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6659     },
6660
6661     // private
6662     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6663     validateBlur : function(e, t){
6664         return true;
6665     },
6666
6667     // private
6668     onDisable : function(){
6669         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6670         //if(this.wrap){
6671         //    this.wrap.addClass('x-item-disabled');
6672         //}
6673     },
6674
6675     // private
6676     onEnable : function(){
6677         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6678         //if(this.wrap){
6679         //    this.el.removeClass('x-item-disabled');
6680         //}
6681     },
6682
6683     // private
6684     onShow : function(){
6685         var ae = this.getActionEl();
6686         
6687         if(ae){
6688             ae.dom.style.display = '';
6689             ae.dom.style.visibility = 'visible';
6690         }
6691     },
6692
6693     // private
6694     
6695     onHide : function(){
6696         var ae = this.getActionEl();
6697         ae.dom.style.display = 'none';
6698     },
6699
6700     /**
6701      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6702      * by an implementing function.
6703      * @method
6704      * @param {EventObject} e
6705      */
6706     onTriggerClick : Roo.emptyFn
6707 });
6708  /*
6709  * Based on:
6710  * Ext JS Library 1.1.1
6711  * Copyright(c) 2006-2007, Ext JS, LLC.
6712  *
6713  * Originally Released Under LGPL - original licence link has changed is not relivant.
6714  *
6715  * Fork - LGPL
6716  * <script type="text/javascript">
6717  */
6718
6719
6720 /**
6721  * @class Roo.data.SortTypes
6722  * @singleton
6723  * Defines the default sorting (casting?) comparison functions used when sorting data.
6724  */
6725 Roo.data.SortTypes = {
6726     /**
6727      * Default sort that does nothing
6728      * @param {Mixed} s The value being converted
6729      * @return {Mixed} The comparison value
6730      */
6731     none : function(s){
6732         return s;
6733     },
6734     
6735     /**
6736      * The regular expression used to strip tags
6737      * @type {RegExp}
6738      * @property
6739      */
6740     stripTagsRE : /<\/?[^>]+>/gi,
6741     
6742     /**
6743      * Strips all HTML tags to sort on text only
6744      * @param {Mixed} s The value being converted
6745      * @return {String} The comparison value
6746      */
6747     asText : function(s){
6748         return String(s).replace(this.stripTagsRE, "");
6749     },
6750     
6751     /**
6752      * Strips all HTML tags to sort on text only - Case insensitive
6753      * @param {Mixed} s The value being converted
6754      * @return {String} The comparison value
6755      */
6756     asUCText : function(s){
6757         return String(s).toUpperCase().replace(this.stripTagsRE, "");
6758     },
6759     
6760     /**
6761      * Case insensitive string
6762      * @param {Mixed} s The value being converted
6763      * @return {String} The comparison value
6764      */
6765     asUCString : function(s) {
6766         return String(s).toUpperCase();
6767     },
6768     
6769     /**
6770      * Date sorting
6771      * @param {Mixed} s The value being converted
6772      * @return {Number} The comparison value
6773      */
6774     asDate : function(s) {
6775         if(!s){
6776             return 0;
6777         }
6778         if(s instanceof Date){
6779             return s.getTime();
6780         }
6781         return Date.parse(String(s));
6782     },
6783     
6784     /**
6785      * Float sorting
6786      * @param {Mixed} s The value being converted
6787      * @return {Float} The comparison value
6788      */
6789     asFloat : function(s) {
6790         var val = parseFloat(String(s).replace(/,/g, ""));
6791         if(isNaN(val)) val = 0;
6792         return val;
6793     },
6794     
6795     /**
6796      * Integer sorting
6797      * @param {Mixed} s The value being converted
6798      * @return {Number} The comparison value
6799      */
6800     asInt : function(s) {
6801         var val = parseInt(String(s).replace(/,/g, ""));
6802         if(isNaN(val)) val = 0;
6803         return val;
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 * @class Roo.data.Record
6818  * Instances of this class encapsulate both record <em>definition</em> information, and record
6819  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
6820  * to access Records cached in an {@link Roo.data.Store} object.<br>
6821  * <p>
6822  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
6823  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
6824  * objects.<br>
6825  * <p>
6826  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
6827  * @constructor
6828  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
6829  * {@link #create}. The parameters are the same.
6830  * @param {Array} data An associative Array of data values keyed by the field name.
6831  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
6832  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
6833  * not specified an integer id is generated.
6834  */
6835 Roo.data.Record = function(data, id){
6836     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
6837     this.data = data;
6838 };
6839
6840 /**
6841  * Generate a constructor for a specific record layout.
6842  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
6843  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
6844  * Each field definition object may contain the following properties: <ul>
6845  * <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,
6846  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
6847  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
6848  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
6849  * is being used, then this is a string containing the javascript expression to reference the data relative to 
6850  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
6851  * to the data item relative to the record element. If the mapping expression is the same as the field name,
6852  * this may be omitted.</p></li>
6853  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
6854  * <ul><li>auto (Default, implies no conversion)</li>
6855  * <li>string</li>
6856  * <li>int</li>
6857  * <li>float</li>
6858  * <li>boolean</li>
6859  * <li>date</li></ul></p></li>
6860  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6861  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6862  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6863  * by the Reader into an object that will be stored in the Record. It is passed the
6864  * following parameters:<ul>
6865  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6866  * </ul></p></li>
6867  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6868  * </ul>
6869  * <br>usage:<br><pre><code>
6870 var TopicRecord = Roo.data.Record.create(
6871     {name: 'title', mapping: 'topic_title'},
6872     {name: 'author', mapping: 'username'},
6873     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6874     {name: 'lastPost', mapping: 'post_time', type: 'date'},
6875     {name: 'lastPoster', mapping: 'user2'},
6876     {name: 'excerpt', mapping: 'post_text'}
6877 );
6878
6879 var myNewRecord = new TopicRecord({
6880     title: 'Do my job please',
6881     author: 'noobie',
6882     totalPosts: 1,
6883     lastPost: new Date(),
6884     lastPoster: 'Animal',
6885     excerpt: 'No way dude!'
6886 });
6887 myStore.add(myNewRecord);
6888 </code></pre>
6889  * @method create
6890  * @static
6891  */
6892 Roo.data.Record.create = function(o){
6893     var f = function(){
6894         f.superclass.constructor.apply(this, arguments);
6895     };
6896     Roo.extend(f, Roo.data.Record);
6897     var p = f.prototype;
6898     p.fields = new Roo.util.MixedCollection(false, function(field){
6899         return field.name;
6900     });
6901     for(var i = 0, len = o.length; i < len; i++){
6902         p.fields.add(new Roo.data.Field(o[i]));
6903     }
6904     f.getField = function(name){
6905         return p.fields.get(name);  
6906     };
6907     return f;
6908 };
6909
6910 Roo.data.Record.AUTO_ID = 1000;
6911 Roo.data.Record.EDIT = 'edit';
6912 Roo.data.Record.REJECT = 'reject';
6913 Roo.data.Record.COMMIT = 'commit';
6914
6915 Roo.data.Record.prototype = {
6916     /**
6917      * Readonly flag - true if this record has been modified.
6918      * @type Boolean
6919      */
6920     dirty : false,
6921     editing : false,
6922     error: null,
6923     modified: null,
6924
6925     // private
6926     join : function(store){
6927         this.store = store;
6928     },
6929
6930     /**
6931      * Set the named field to the specified value.
6932      * @param {String} name The name of the field to set.
6933      * @param {Object} value The value to set the field to.
6934      */
6935     set : function(name, value){
6936         if(this.data[name] == value){
6937             return;
6938         }
6939         this.dirty = true;
6940         if(!this.modified){
6941             this.modified = {};
6942         }
6943         if(typeof this.modified[name] == 'undefined'){
6944             this.modified[name] = this.data[name];
6945         }
6946         this.data[name] = value;
6947         if(!this.editing && this.store){
6948             this.store.afterEdit(this);
6949         }       
6950     },
6951
6952     /**
6953      * Get the value of the named field.
6954      * @param {String} name The name of the field to get the value of.
6955      * @return {Object} The value of the field.
6956      */
6957     get : function(name){
6958         return this.data[name]; 
6959     },
6960
6961     // private
6962     beginEdit : function(){
6963         this.editing = true;
6964         this.modified = {}; 
6965     },
6966
6967     // private
6968     cancelEdit : function(){
6969         this.editing = false;
6970         delete this.modified;
6971     },
6972
6973     // private
6974     endEdit : function(){
6975         this.editing = false;
6976         if(this.dirty && this.store){
6977             this.store.afterEdit(this);
6978         }
6979     },
6980
6981     /**
6982      * Usually called by the {@link Roo.data.Store} which owns the Record.
6983      * Rejects all changes made to the Record since either creation, or the last commit operation.
6984      * Modified fields are reverted to their original values.
6985      * <p>
6986      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6987      * of reject operations.
6988      */
6989     reject : function(){
6990         var m = this.modified;
6991         for(var n in m){
6992             if(typeof m[n] != "function"){
6993                 this.data[n] = m[n];
6994             }
6995         }
6996         this.dirty = false;
6997         delete this.modified;
6998         this.editing = false;
6999         if(this.store){
7000             this.store.afterReject(this);
7001         }
7002     },
7003
7004     /**
7005      * Usually called by the {@link Roo.data.Store} which owns the Record.
7006      * Commits all changes made to the Record since either creation, or the last commit operation.
7007      * <p>
7008      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7009      * of commit operations.
7010      */
7011     commit : function(){
7012         this.dirty = false;
7013         delete this.modified;
7014         this.editing = false;
7015         if(this.store){
7016             this.store.afterCommit(this);
7017         }
7018     },
7019
7020     // private
7021     hasError : function(){
7022         return this.error != null;
7023     },
7024
7025     // private
7026     clearError : function(){
7027         this.error = null;
7028     },
7029
7030     /**
7031      * Creates a copy of this record.
7032      * @param {String} id (optional) A new record id if you don't want to use this record's id
7033      * @return {Record}
7034      */
7035     copy : function(newId) {
7036         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7037     }
7038 };/*
7039  * Based on:
7040  * Ext JS Library 1.1.1
7041  * Copyright(c) 2006-2007, Ext JS, LLC.
7042  *
7043  * Originally Released Under LGPL - original licence link has changed is not relivant.
7044  *
7045  * Fork - LGPL
7046  * <script type="text/javascript">
7047  */
7048
7049
7050
7051 /**
7052  * @class Roo.data.Store
7053  * @extends Roo.util.Observable
7054  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7055  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7056  * <p>
7057  * 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
7058  * has no knowledge of the format of the data returned by the Proxy.<br>
7059  * <p>
7060  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7061  * instances from the data object. These records are cached and made available through accessor functions.
7062  * @constructor
7063  * Creates a new Store.
7064  * @param {Object} config A config object containing the objects needed for the Store to access data,
7065  * and read the data into Records.
7066  */
7067 Roo.data.Store = function(config){
7068     this.data = new Roo.util.MixedCollection(false);
7069     this.data.getKey = function(o){
7070         return o.id;
7071     };
7072     this.baseParams = {};
7073     // private
7074     this.paramNames = {
7075         "start" : "start",
7076         "limit" : "limit",
7077         "sort" : "sort",
7078         "dir" : "dir",
7079         "multisort" : "_multisort"
7080     };
7081
7082     if(config && config.data){
7083         this.inlineData = config.data;
7084         delete config.data;
7085     }
7086
7087     Roo.apply(this, config);
7088     
7089     if(this.reader){ // reader passed
7090         this.reader = Roo.factory(this.reader, Roo.data);
7091         this.reader.xmodule = this.xmodule || false;
7092         if(!this.recordType){
7093             this.recordType = this.reader.recordType;
7094         }
7095         if(this.reader.onMetaChange){
7096             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7097         }
7098     }
7099
7100     if(this.recordType){
7101         this.fields = this.recordType.prototype.fields;
7102     }
7103     this.modified = [];
7104
7105     this.addEvents({
7106         /**
7107          * @event datachanged
7108          * Fires when the data cache has changed, and a widget which is using this Store
7109          * as a Record cache should refresh its view.
7110          * @param {Store} this
7111          */
7112         datachanged : true,
7113         /**
7114          * @event metachange
7115          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7116          * @param {Store} this
7117          * @param {Object} meta The JSON metadata
7118          */
7119         metachange : true,
7120         /**
7121          * @event add
7122          * Fires when Records have been added to the Store
7123          * @param {Store} this
7124          * @param {Roo.data.Record[]} records The array of Records added
7125          * @param {Number} index The index at which the record(s) were added
7126          */
7127         add : true,
7128         /**
7129          * @event remove
7130          * Fires when a Record has been removed from the Store
7131          * @param {Store} this
7132          * @param {Roo.data.Record} record The Record that was removed
7133          * @param {Number} index The index at which the record was removed
7134          */
7135         remove : true,
7136         /**
7137          * @event update
7138          * Fires when a Record has been updated
7139          * @param {Store} this
7140          * @param {Roo.data.Record} record The Record that was updated
7141          * @param {String} operation The update operation being performed.  Value may be one of:
7142          * <pre><code>
7143  Roo.data.Record.EDIT
7144  Roo.data.Record.REJECT
7145  Roo.data.Record.COMMIT
7146          * </code></pre>
7147          */
7148         update : true,
7149         /**
7150          * @event clear
7151          * Fires when the data cache has been cleared.
7152          * @param {Store} this
7153          */
7154         clear : true,
7155         /**
7156          * @event beforeload
7157          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7158          * the load action will be canceled.
7159          * @param {Store} this
7160          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7161          */
7162         beforeload : true,
7163         /**
7164          * @event beforeloadadd
7165          * Fires after a new set of Records has been loaded.
7166          * @param {Store} this
7167          * @param {Roo.data.Record[]} records The Records that were loaded
7168          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7169          */
7170         beforeloadadd : true,
7171         /**
7172          * @event load
7173          * Fires after a new set of Records has been loaded, before they are added to the store.
7174          * @param {Store} this
7175          * @param {Roo.data.Record[]} records The Records that were loaded
7176          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7177          * @params {Object} return from reader
7178          */
7179         load : true,
7180         /**
7181          * @event loadexception
7182          * Fires if an exception occurs in the Proxy during loading.
7183          * Called with the signature of the Proxy's "loadexception" event.
7184          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7185          * 
7186          * @param {Proxy} 
7187          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7188          * @param {Object} load options 
7189          * @param {Object} jsonData from your request (normally this contains the Exception)
7190          */
7191         loadexception : true
7192     });
7193     
7194     if(this.proxy){
7195         this.proxy = Roo.factory(this.proxy, Roo.data);
7196         this.proxy.xmodule = this.xmodule || false;
7197         this.relayEvents(this.proxy,  ["loadexception"]);
7198     }
7199     this.sortToggle = {};
7200     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7201
7202     Roo.data.Store.superclass.constructor.call(this);
7203
7204     if(this.inlineData){
7205         this.loadData(this.inlineData);
7206         delete this.inlineData;
7207     }
7208 };
7209
7210 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7211      /**
7212     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7213     * without a remote query - used by combo/forms at present.
7214     */
7215     
7216     /**
7217     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7218     */
7219     /**
7220     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7221     */
7222     /**
7223     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7224     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7225     */
7226     /**
7227     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7228     * on any HTTP request
7229     */
7230     /**
7231     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7232     */
7233     /**
7234     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7235     */
7236     multiSort: false,
7237     /**
7238     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7239     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7240     */
7241     remoteSort : false,
7242
7243     /**
7244     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7245      * loaded or when a record is removed. (defaults to false).
7246     */
7247     pruneModifiedRecords : false,
7248
7249     // private
7250     lastOptions : null,
7251
7252     /**
7253      * Add Records to the Store and fires the add event.
7254      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7255      */
7256     add : function(records){
7257         records = [].concat(records);
7258         for(var i = 0, len = records.length; i < len; i++){
7259             records[i].join(this);
7260         }
7261         var index = this.data.length;
7262         this.data.addAll(records);
7263         this.fireEvent("add", this, records, index);
7264     },
7265
7266     /**
7267      * Remove a Record from the Store and fires the remove event.
7268      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7269      */
7270     remove : function(record){
7271         var index = this.data.indexOf(record);
7272         this.data.removeAt(index);
7273         if(this.pruneModifiedRecords){
7274             this.modified.remove(record);
7275         }
7276         this.fireEvent("remove", this, record, index);
7277     },
7278
7279     /**
7280      * Remove all Records from the Store and fires the clear event.
7281      */
7282     removeAll : function(){
7283         this.data.clear();
7284         if(this.pruneModifiedRecords){
7285             this.modified = [];
7286         }
7287         this.fireEvent("clear", this);
7288     },
7289
7290     /**
7291      * Inserts Records to the Store at the given index and fires the add event.
7292      * @param {Number} index The start index at which to insert the passed Records.
7293      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7294      */
7295     insert : function(index, records){
7296         records = [].concat(records);
7297         for(var i = 0, len = records.length; i < len; i++){
7298             this.data.insert(index, records[i]);
7299             records[i].join(this);
7300         }
7301         this.fireEvent("add", this, records, index);
7302     },
7303
7304     /**
7305      * Get the index within the cache of the passed Record.
7306      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7307      * @return {Number} The index of the passed Record. Returns -1 if not found.
7308      */
7309     indexOf : function(record){
7310         return this.data.indexOf(record);
7311     },
7312
7313     /**
7314      * Get the index within the cache of the Record with the passed id.
7315      * @param {String} id The id of the Record to find.
7316      * @return {Number} The index of the Record. Returns -1 if not found.
7317      */
7318     indexOfId : function(id){
7319         return this.data.indexOfKey(id);
7320     },
7321
7322     /**
7323      * Get the Record with the specified id.
7324      * @param {String} id The id of the Record to find.
7325      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7326      */
7327     getById : function(id){
7328         return this.data.key(id);
7329     },
7330
7331     /**
7332      * Get the Record at the specified index.
7333      * @param {Number} index The index of the Record to find.
7334      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7335      */
7336     getAt : function(index){
7337         return this.data.itemAt(index);
7338     },
7339
7340     /**
7341      * Returns a range of Records between specified indices.
7342      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7343      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7344      * @return {Roo.data.Record[]} An array of Records
7345      */
7346     getRange : function(start, end){
7347         return this.data.getRange(start, end);
7348     },
7349
7350     // private
7351     storeOptions : function(o){
7352         o = Roo.apply({}, o);
7353         delete o.callback;
7354         delete o.scope;
7355         this.lastOptions = o;
7356     },
7357
7358     /**
7359      * Loads the Record cache from the configured Proxy using the configured Reader.
7360      * <p>
7361      * If using remote paging, then the first load call must specify the <em>start</em>
7362      * and <em>limit</em> properties in the options.params property to establish the initial
7363      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7364      * <p>
7365      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7366      * and this call will return before the new data has been loaded. Perform any post-processing
7367      * in a callback function, or in a "load" event handler.</strong>
7368      * <p>
7369      * @param {Object} options An object containing properties which control loading options:<ul>
7370      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7371      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7372      * passed the following arguments:<ul>
7373      * <li>r : Roo.data.Record[]</li>
7374      * <li>options: Options object from the load call</li>
7375      * <li>success: Boolean success indicator</li></ul></li>
7376      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7377      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7378      * </ul>
7379      */
7380     load : function(options){
7381         options = options || {};
7382         if(this.fireEvent("beforeload", this, options) !== false){
7383             this.storeOptions(options);
7384             var p = Roo.apply(options.params || {}, this.baseParams);
7385             // if meta was not loaded from remote source.. try requesting it.
7386             if (!this.reader.metaFromRemote) {
7387                 p._requestMeta = 1;
7388             }
7389             if(this.sortInfo && this.remoteSort){
7390                 var pn = this.paramNames;
7391                 p[pn["sort"]] = this.sortInfo.field;
7392                 p[pn["dir"]] = this.sortInfo.direction;
7393             }
7394             if (this.multiSort) {
7395                 var pn = this.paramNames;
7396                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7397             }
7398             
7399             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7400         }
7401     },
7402
7403     /**
7404      * Reloads the Record cache from the configured Proxy using the configured Reader and
7405      * the options from the last load operation performed.
7406      * @param {Object} options (optional) An object containing properties which may override the options
7407      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7408      * the most recently used options are reused).
7409      */
7410     reload : function(options){
7411         this.load(Roo.applyIf(options||{}, this.lastOptions));
7412     },
7413
7414     // private
7415     // Called as a callback by the Reader during a load operation.
7416     loadRecords : function(o, options, success){
7417         if(!o || success === false){
7418             if(success !== false){
7419                 this.fireEvent("load", this, [], options, o);
7420             }
7421             if(options.callback){
7422                 options.callback.call(options.scope || this, [], options, false);
7423             }
7424             return;
7425         }
7426         // if data returned failure - throw an exception.
7427         if (o.success === false) {
7428             // show a message if no listener is registered.
7429             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7430                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7431             }
7432             // loadmask wil be hooked into this..
7433             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7434             return;
7435         }
7436         var r = o.records, t = o.totalRecords || r.length;
7437         
7438         this.fireEvent("beforeloadadd", this, r, options, o);
7439         
7440         if(!options || options.add !== true){
7441             if(this.pruneModifiedRecords){
7442                 this.modified = [];
7443             }
7444             for(var i = 0, len = r.length; i < len; i++){
7445                 r[i].join(this);
7446             }
7447             if(this.snapshot){
7448                 this.data = this.snapshot;
7449                 delete this.snapshot;
7450             }
7451             this.data.clear();
7452             this.data.addAll(r);
7453             this.totalLength = t;
7454             this.applySort();
7455             this.fireEvent("datachanged", this);
7456         }else{
7457             this.totalLength = Math.max(t, this.data.length+r.length);
7458             this.add(r);
7459         }
7460         this.fireEvent("load", this, r, options, o);
7461         if(options.callback){
7462             options.callback.call(options.scope || this, r, options, true);
7463         }
7464     },
7465
7466
7467     /**
7468      * Loads data from a passed data block. A Reader which understands the format of the data
7469      * must have been configured in the constructor.
7470      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7471      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7472      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7473      */
7474     loadData : function(o, append){
7475         var r = this.reader.readRecords(o);
7476         this.loadRecords(r, {add: append}, true);
7477     },
7478
7479     /**
7480      * Gets the number of cached records.
7481      * <p>
7482      * <em>If using paging, this may not be the total size of the dataset. If the data object
7483      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7484      * the data set size</em>
7485      */
7486     getCount : function(){
7487         return this.data.length || 0;
7488     },
7489
7490     /**
7491      * Gets the total number of records in the dataset as returned by the server.
7492      * <p>
7493      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7494      * the dataset size</em>
7495      */
7496     getTotalCount : function(){
7497         return this.totalLength || 0;
7498     },
7499
7500     /**
7501      * Returns the sort state of the Store as an object with two properties:
7502      * <pre><code>
7503  field {String} The name of the field by which the Records are sorted
7504  direction {String} The sort order, "ASC" or "DESC"
7505      * </code></pre>
7506      */
7507     getSortState : function(){
7508         return this.sortInfo;
7509     },
7510
7511     // private
7512     applySort : function(){
7513         if(this.sortInfo && !this.remoteSort){
7514             var s = this.sortInfo, f = s.field;
7515             var st = this.fields.get(f).sortType;
7516             var fn = function(r1, r2){
7517                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7518                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7519             };
7520             this.data.sort(s.direction, fn);
7521             if(this.snapshot && this.snapshot != this.data){
7522                 this.snapshot.sort(s.direction, fn);
7523             }
7524         }
7525     },
7526
7527     /**
7528      * Sets the default sort column and order to be used by the next load operation.
7529      * @param {String} fieldName The name of the field to sort by.
7530      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7531      */
7532     setDefaultSort : function(field, dir){
7533         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7534     },
7535
7536     /**
7537      * Sort the Records.
7538      * If remote sorting is used, the sort is performed on the server, and the cache is
7539      * reloaded. If local sorting is used, the cache is sorted internally.
7540      * @param {String} fieldName The name of the field to sort by.
7541      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7542      */
7543     sort : function(fieldName, dir){
7544         var f = this.fields.get(fieldName);
7545         if(!dir){
7546             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7547             
7548             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7549                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7550             }else{
7551                 dir = f.sortDir;
7552             }
7553         }
7554         this.sortToggle[f.name] = dir;
7555         this.sortInfo = {field: f.name, direction: dir};
7556         if(!this.remoteSort){
7557             this.applySort();
7558             this.fireEvent("datachanged", this);
7559         }else{
7560             this.load(this.lastOptions);
7561         }
7562     },
7563
7564     /**
7565      * Calls the specified function for each of the Records in the cache.
7566      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7567      * Returning <em>false</em> aborts and exits the iteration.
7568      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7569      */
7570     each : function(fn, scope){
7571         this.data.each(fn, scope);
7572     },
7573
7574     /**
7575      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7576      * (e.g., during paging).
7577      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7578      */
7579     getModifiedRecords : function(){
7580         return this.modified;
7581     },
7582
7583     // private
7584     createFilterFn : function(property, value, anyMatch){
7585         if(!value.exec){ // not a regex
7586             value = String(value);
7587             if(value.length == 0){
7588                 return false;
7589             }
7590             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7591         }
7592         return function(r){
7593             return value.test(r.data[property]);
7594         };
7595     },
7596
7597     /**
7598      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7599      * @param {String} property A field on your records
7600      * @param {Number} start The record index to start at (defaults to 0)
7601      * @param {Number} end The last record index to include (defaults to length - 1)
7602      * @return {Number} The sum
7603      */
7604     sum : function(property, start, end){
7605         var rs = this.data.items, v = 0;
7606         start = start || 0;
7607         end = (end || end === 0) ? end : rs.length-1;
7608
7609         for(var i = start; i <= end; i++){
7610             v += (rs[i].data[property] || 0);
7611         }
7612         return v;
7613     },
7614
7615     /**
7616      * Filter the records by a specified property.
7617      * @param {String} field A field on your records
7618      * @param {String/RegExp} value Either a string that the field
7619      * should start with or a RegExp to test against the field
7620      * @param {Boolean} anyMatch True to match any part not just the beginning
7621      */
7622     filter : function(property, value, anyMatch){
7623         var fn = this.createFilterFn(property, value, anyMatch);
7624         return fn ? this.filterBy(fn) : this.clearFilter();
7625     },
7626
7627     /**
7628      * Filter by a function. The specified function will be called with each
7629      * record in this data source. If the function returns true the record is included,
7630      * otherwise it is filtered.
7631      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7632      * @param {Object} scope (optional) The scope of the function (defaults to this)
7633      */
7634     filterBy : function(fn, scope){
7635         this.snapshot = this.snapshot || this.data;
7636         this.data = this.queryBy(fn, scope||this);
7637         this.fireEvent("datachanged", this);
7638     },
7639
7640     /**
7641      * Query the records by a specified property.
7642      * @param {String} field A field on your records
7643      * @param {String/RegExp} value Either a string that the field
7644      * should start with or a RegExp to test against the field
7645      * @param {Boolean} anyMatch True to match any part not just the beginning
7646      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7647      */
7648     query : function(property, value, anyMatch){
7649         var fn = this.createFilterFn(property, value, anyMatch);
7650         return fn ? this.queryBy(fn) : this.data.clone();
7651     },
7652
7653     /**
7654      * Query by a function. The specified function will be called with each
7655      * record in this data source. If the function returns true the record is included
7656      * in the results.
7657      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7658      * @param {Object} scope (optional) The scope of the function (defaults to this)
7659       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7660      **/
7661     queryBy : function(fn, scope){
7662         var data = this.snapshot || this.data;
7663         return data.filterBy(fn, scope||this);
7664     },
7665
7666     /**
7667      * Collects unique values for a particular dataIndex from this store.
7668      * @param {String} dataIndex The property to collect
7669      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7670      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7671      * @return {Array} An array of the unique values
7672      **/
7673     collect : function(dataIndex, allowNull, bypassFilter){
7674         var d = (bypassFilter === true && this.snapshot) ?
7675                 this.snapshot.items : this.data.items;
7676         var v, sv, r = [], l = {};
7677         for(var i = 0, len = d.length; i < len; i++){
7678             v = d[i].data[dataIndex];
7679             sv = String(v);
7680             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7681                 l[sv] = true;
7682                 r[r.length] = v;
7683             }
7684         }
7685         return r;
7686     },
7687
7688     /**
7689      * Revert to a view of the Record cache with no filtering applied.
7690      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7691      */
7692     clearFilter : function(suppressEvent){
7693         if(this.snapshot && this.snapshot != this.data){
7694             this.data = this.snapshot;
7695             delete this.snapshot;
7696             if(suppressEvent !== true){
7697                 this.fireEvent("datachanged", this);
7698             }
7699         }
7700     },
7701
7702     // private
7703     afterEdit : function(record){
7704         if(this.modified.indexOf(record) == -1){
7705             this.modified.push(record);
7706         }
7707         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7708     },
7709     
7710     // private
7711     afterReject : function(record){
7712         this.modified.remove(record);
7713         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7714     },
7715
7716     // private
7717     afterCommit : function(record){
7718         this.modified.remove(record);
7719         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7720     },
7721
7722     /**
7723      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7724      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7725      */
7726     commitChanges : function(){
7727         var m = this.modified.slice(0);
7728         this.modified = [];
7729         for(var i = 0, len = m.length; i < len; i++){
7730             m[i].commit();
7731         }
7732     },
7733
7734     /**
7735      * Cancel outstanding changes on all changed records.
7736      */
7737     rejectChanges : function(){
7738         var m = this.modified.slice(0);
7739         this.modified = [];
7740         for(var i = 0, len = m.length; i < len; i++){
7741             m[i].reject();
7742         }
7743     },
7744
7745     onMetaChange : function(meta, rtype, o){
7746         this.recordType = rtype;
7747         this.fields = rtype.prototype.fields;
7748         delete this.snapshot;
7749         this.sortInfo = meta.sortInfo || this.sortInfo;
7750         this.modified = [];
7751         this.fireEvent('metachange', this, this.reader.meta);
7752     },
7753     
7754     moveIndex : function(data, type)
7755     {
7756         var index = this.indexOf(data);
7757         
7758         var newIndex = index + type;
7759         
7760         this.remove(data);
7761         
7762         this.insert(newIndex, data);
7763         
7764     }
7765 });/*
7766  * Based on:
7767  * Ext JS Library 1.1.1
7768  * Copyright(c) 2006-2007, Ext JS, LLC.
7769  *
7770  * Originally Released Under LGPL - original licence link has changed is not relivant.
7771  *
7772  * Fork - LGPL
7773  * <script type="text/javascript">
7774  */
7775
7776 /**
7777  * @class Roo.data.SimpleStore
7778  * @extends Roo.data.Store
7779  * Small helper class to make creating Stores from Array data easier.
7780  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7781  * @cfg {Array} fields An array of field definition objects, or field name strings.
7782  * @cfg {Array} data The multi-dimensional array of data
7783  * @constructor
7784  * @param {Object} config
7785  */
7786 Roo.data.SimpleStore = function(config){
7787     Roo.data.SimpleStore.superclass.constructor.call(this, {
7788         isLocal : true,
7789         reader: new Roo.data.ArrayReader({
7790                 id: config.id
7791             },
7792             Roo.data.Record.create(config.fields)
7793         ),
7794         proxy : new Roo.data.MemoryProxy(config.data)
7795     });
7796     this.load();
7797 };
7798 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7799  * Based on:
7800  * Ext JS Library 1.1.1
7801  * Copyright(c) 2006-2007, Ext JS, LLC.
7802  *
7803  * Originally Released Under LGPL - original licence link has changed is not relivant.
7804  *
7805  * Fork - LGPL
7806  * <script type="text/javascript">
7807  */
7808
7809 /**
7810 /**
7811  * @extends Roo.data.Store
7812  * @class Roo.data.JsonStore
7813  * Small helper class to make creating Stores for JSON data easier. <br/>
7814 <pre><code>
7815 var store = new Roo.data.JsonStore({
7816     url: 'get-images.php',
7817     root: 'images',
7818     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
7819 });
7820 </code></pre>
7821  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
7822  * JsonReader and HttpProxy (unless inline data is provided).</b>
7823  * @cfg {Array} fields An array of field definition objects, or field name strings.
7824  * @constructor
7825  * @param {Object} config
7826  */
7827 Roo.data.JsonStore = function(c){
7828     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
7829         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
7830         reader: new Roo.data.JsonReader(c, c.fields)
7831     }));
7832 };
7833 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
7834  * Based on:
7835  * Ext JS Library 1.1.1
7836  * Copyright(c) 2006-2007, Ext JS, LLC.
7837  *
7838  * Originally Released Under LGPL - original licence link has changed is not relivant.
7839  *
7840  * Fork - LGPL
7841  * <script type="text/javascript">
7842  */
7843
7844  
7845 Roo.data.Field = function(config){
7846     if(typeof config == "string"){
7847         config = {name: config};
7848     }
7849     Roo.apply(this, config);
7850     
7851     if(!this.type){
7852         this.type = "auto";
7853     }
7854     
7855     var st = Roo.data.SortTypes;
7856     // named sortTypes are supported, here we look them up
7857     if(typeof this.sortType == "string"){
7858         this.sortType = st[this.sortType];
7859     }
7860     
7861     // set default sortType for strings and dates
7862     if(!this.sortType){
7863         switch(this.type){
7864             case "string":
7865                 this.sortType = st.asUCString;
7866                 break;
7867             case "date":
7868                 this.sortType = st.asDate;
7869                 break;
7870             default:
7871                 this.sortType = st.none;
7872         }
7873     }
7874
7875     // define once
7876     var stripRe = /[\$,%]/g;
7877
7878     // prebuilt conversion function for this field, instead of
7879     // switching every time we're reading a value
7880     if(!this.convert){
7881         var cv, dateFormat = this.dateFormat;
7882         switch(this.type){
7883             case "":
7884             case "auto":
7885             case undefined:
7886                 cv = function(v){ return v; };
7887                 break;
7888             case "string":
7889                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7890                 break;
7891             case "int":
7892                 cv = function(v){
7893                     return v !== undefined && v !== null && v !== '' ?
7894                            parseInt(String(v).replace(stripRe, ""), 10) : '';
7895                     };
7896                 break;
7897             case "float":
7898                 cv = function(v){
7899                     return v !== undefined && v !== null && v !== '' ?
7900                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
7901                     };
7902                 break;
7903             case "bool":
7904             case "boolean":
7905                 cv = function(v){ return v === true || v === "true" || v == 1; };
7906                 break;
7907             case "date":
7908                 cv = function(v){
7909                     if(!v){
7910                         return '';
7911                     }
7912                     if(v instanceof Date){
7913                         return v;
7914                     }
7915                     if(dateFormat){
7916                         if(dateFormat == "timestamp"){
7917                             return new Date(v*1000);
7918                         }
7919                         return Date.parseDate(v, dateFormat);
7920                     }
7921                     var parsed = Date.parse(v);
7922                     return parsed ? new Date(parsed) : null;
7923                 };
7924              break;
7925             
7926         }
7927         this.convert = cv;
7928     }
7929 };
7930
7931 Roo.data.Field.prototype = {
7932     dateFormat: null,
7933     defaultValue: "",
7934     mapping: null,
7935     sortType : null,
7936     sortDir : "ASC"
7937 };/*
7938  * Based on:
7939  * Ext JS Library 1.1.1
7940  * Copyright(c) 2006-2007, Ext JS, LLC.
7941  *
7942  * Originally Released Under LGPL - original licence link has changed is not relivant.
7943  *
7944  * Fork - LGPL
7945  * <script type="text/javascript">
7946  */
7947  
7948 // Base class for reading structured data from a data source.  This class is intended to be
7949 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7950
7951 /**
7952  * @class Roo.data.DataReader
7953  * Base class for reading structured data from a data source.  This class is intended to be
7954  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7955  */
7956
7957 Roo.data.DataReader = function(meta, recordType){
7958     
7959     this.meta = meta;
7960     
7961     this.recordType = recordType instanceof Array ? 
7962         Roo.data.Record.create(recordType) : recordType;
7963 };
7964
7965 Roo.data.DataReader.prototype = {
7966      /**
7967      * Create an empty record
7968      * @param {Object} data (optional) - overlay some values
7969      * @return {Roo.data.Record} record created.
7970      */
7971     newRow :  function(d) {
7972         var da =  {};
7973         this.recordType.prototype.fields.each(function(c) {
7974             switch( c.type) {
7975                 case 'int' : da[c.name] = 0; break;
7976                 case 'date' : da[c.name] = new Date(); break;
7977                 case 'float' : da[c.name] = 0.0; break;
7978                 case 'boolean' : da[c.name] = false; break;
7979                 default : da[c.name] = ""; break;
7980             }
7981             
7982         });
7983         return new this.recordType(Roo.apply(da, d));
7984     }
7985     
7986 };/*
7987  * Based on:
7988  * Ext JS Library 1.1.1
7989  * Copyright(c) 2006-2007, Ext JS, LLC.
7990  *
7991  * Originally Released Under LGPL - original licence link has changed is not relivant.
7992  *
7993  * Fork - LGPL
7994  * <script type="text/javascript">
7995  */
7996
7997 /**
7998  * @class Roo.data.DataProxy
7999  * @extends Roo.data.Observable
8000  * This class is an abstract base class for implementations which provide retrieval of
8001  * unformatted data objects.<br>
8002  * <p>
8003  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8004  * (of the appropriate type which knows how to parse the data object) to provide a block of
8005  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8006  * <p>
8007  * Custom implementations must implement the load method as described in
8008  * {@link Roo.data.HttpProxy#load}.
8009  */
8010 Roo.data.DataProxy = function(){
8011     this.addEvents({
8012         /**
8013          * @event beforeload
8014          * Fires before a network request is made to retrieve a data object.
8015          * @param {Object} This DataProxy object.
8016          * @param {Object} params The params parameter to the load function.
8017          */
8018         beforeload : true,
8019         /**
8020          * @event load
8021          * Fires before the load method's callback is called.
8022          * @param {Object} This DataProxy object.
8023          * @param {Object} o The data object.
8024          * @param {Object} arg The callback argument object passed to the load function.
8025          */
8026         load : true,
8027         /**
8028          * @event loadexception
8029          * Fires if an Exception occurs during data retrieval.
8030          * @param {Object} This DataProxy object.
8031          * @param {Object} o The data object.
8032          * @param {Object} arg The callback argument object passed to the load function.
8033          * @param {Object} e The Exception.
8034          */
8035         loadexception : true
8036     });
8037     Roo.data.DataProxy.superclass.constructor.call(this);
8038 };
8039
8040 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8041
8042     /**
8043      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8044      */
8045 /*
8046  * Based on:
8047  * Ext JS Library 1.1.1
8048  * Copyright(c) 2006-2007, Ext JS, LLC.
8049  *
8050  * Originally Released Under LGPL - original licence link has changed is not relivant.
8051  *
8052  * Fork - LGPL
8053  * <script type="text/javascript">
8054  */
8055 /**
8056  * @class Roo.data.MemoryProxy
8057  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8058  * to the Reader when its load method is called.
8059  * @constructor
8060  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8061  */
8062 Roo.data.MemoryProxy = function(data){
8063     if (data.data) {
8064         data = data.data;
8065     }
8066     Roo.data.MemoryProxy.superclass.constructor.call(this);
8067     this.data = data;
8068 };
8069
8070 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8071     /**
8072      * Load data from the requested source (in this case an in-memory
8073      * data object passed to the constructor), read the data object into
8074      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8075      * process that block using the passed callback.
8076      * @param {Object} params This parameter is not used by the MemoryProxy class.
8077      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8078      * object into a block of Roo.data.Records.
8079      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8080      * The function must be passed <ul>
8081      * <li>The Record block object</li>
8082      * <li>The "arg" argument from the load function</li>
8083      * <li>A boolean success indicator</li>
8084      * </ul>
8085      * @param {Object} scope The scope in which to call the callback
8086      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8087      */
8088     load : function(params, reader, callback, scope, arg){
8089         params = params || {};
8090         var result;
8091         try {
8092             result = reader.readRecords(this.data);
8093         }catch(e){
8094             this.fireEvent("loadexception", this, arg, null, e);
8095             callback.call(scope, null, arg, false);
8096             return;
8097         }
8098         callback.call(scope, result, arg, true);
8099     },
8100     
8101     // private
8102     update : function(params, records){
8103         
8104     }
8105 });/*
8106  * Based on:
8107  * Ext JS Library 1.1.1
8108  * Copyright(c) 2006-2007, Ext JS, LLC.
8109  *
8110  * Originally Released Under LGPL - original licence link has changed is not relivant.
8111  *
8112  * Fork - LGPL
8113  * <script type="text/javascript">
8114  */
8115 /**
8116  * @class Roo.data.HttpProxy
8117  * @extends Roo.data.DataProxy
8118  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8119  * configured to reference a certain URL.<br><br>
8120  * <p>
8121  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8122  * from which the running page was served.<br><br>
8123  * <p>
8124  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8125  * <p>
8126  * Be aware that to enable the browser to parse an XML document, the server must set
8127  * the Content-Type header in the HTTP response to "text/xml".
8128  * @constructor
8129  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8130  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8131  * will be used to make the request.
8132  */
8133 Roo.data.HttpProxy = function(conn){
8134     Roo.data.HttpProxy.superclass.constructor.call(this);
8135     // is conn a conn config or a real conn?
8136     this.conn = conn;
8137     this.useAjax = !conn || !conn.events;
8138   
8139 };
8140
8141 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8142     // thse are take from connection...
8143     
8144     /**
8145      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8146      */
8147     /**
8148      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8149      * extra parameters to each request made by this object. (defaults to undefined)
8150      */
8151     /**
8152      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8153      *  to each request made by this object. (defaults to undefined)
8154      */
8155     /**
8156      * @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)
8157      */
8158     /**
8159      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8160      */
8161      /**
8162      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8163      * @type Boolean
8164      */
8165   
8166
8167     /**
8168      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8169      * @type Boolean
8170      */
8171     /**
8172      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8173      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8174      * a finer-grained basis than the DataProxy events.
8175      */
8176     getConnection : function(){
8177         return this.useAjax ? Roo.Ajax : this.conn;
8178     },
8179
8180     /**
8181      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8182      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8183      * process that block using the passed callback.
8184      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8185      * for the request to the remote server.
8186      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8187      * object into a block of Roo.data.Records.
8188      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8189      * The function must be passed <ul>
8190      * <li>The Record block object</li>
8191      * <li>The "arg" argument from the load function</li>
8192      * <li>A boolean success indicator</li>
8193      * </ul>
8194      * @param {Object} scope The scope in which to call the callback
8195      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8196      */
8197     load : function(params, reader, callback, scope, arg){
8198         if(this.fireEvent("beforeload", this, params) !== false){
8199             var  o = {
8200                 params : params || {},
8201                 request: {
8202                     callback : callback,
8203                     scope : scope,
8204                     arg : arg
8205                 },
8206                 reader: reader,
8207                 callback : this.loadResponse,
8208                 scope: this
8209             };
8210             if(this.useAjax){
8211                 Roo.applyIf(o, this.conn);
8212                 if(this.activeRequest){
8213                     Roo.Ajax.abort(this.activeRequest);
8214                 }
8215                 this.activeRequest = Roo.Ajax.request(o);
8216             }else{
8217                 this.conn.request(o);
8218             }
8219         }else{
8220             callback.call(scope||this, null, arg, false);
8221         }
8222     },
8223
8224     // private
8225     loadResponse : function(o, success, response){
8226         delete this.activeRequest;
8227         if(!success){
8228             this.fireEvent("loadexception", this, o, response);
8229             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8230             return;
8231         }
8232         var result;
8233         try {
8234             result = o.reader.read(response);
8235         }catch(e){
8236             this.fireEvent("loadexception", this, o, response, e);
8237             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8238             return;
8239         }
8240         
8241         this.fireEvent("load", this, o, o.request.arg);
8242         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8243     },
8244
8245     // private
8246     update : function(dataSet){
8247
8248     },
8249
8250     // private
8251     updateResponse : function(dataSet){
8252
8253     }
8254 });/*
8255  * Based on:
8256  * Ext JS Library 1.1.1
8257  * Copyright(c) 2006-2007, Ext JS, LLC.
8258  *
8259  * Originally Released Under LGPL - original licence link has changed is not relivant.
8260  *
8261  * Fork - LGPL
8262  * <script type="text/javascript">
8263  */
8264
8265 /**
8266  * @class Roo.data.ScriptTagProxy
8267  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8268  * other than the originating domain of the running page.<br><br>
8269  * <p>
8270  * <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
8271  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8272  * <p>
8273  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8274  * source code that is used as the source inside a &lt;script> tag.<br><br>
8275  * <p>
8276  * In order for the browser to process the returned data, the server must wrap the data object
8277  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8278  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8279  * depending on whether the callback name was passed:
8280  * <p>
8281  * <pre><code>
8282 boolean scriptTag = false;
8283 String cb = request.getParameter("callback");
8284 if (cb != null) {
8285     scriptTag = true;
8286     response.setContentType("text/javascript");
8287 } else {
8288     response.setContentType("application/x-json");
8289 }
8290 Writer out = response.getWriter();
8291 if (scriptTag) {
8292     out.write(cb + "(");
8293 }
8294 out.print(dataBlock.toJsonString());
8295 if (scriptTag) {
8296     out.write(");");
8297 }
8298 </pre></code>
8299  *
8300  * @constructor
8301  * @param {Object} config A configuration object.
8302  */
8303 Roo.data.ScriptTagProxy = function(config){
8304     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8305     Roo.apply(this, config);
8306     this.head = document.getElementsByTagName("head")[0];
8307 };
8308
8309 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8310
8311 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8312     /**
8313      * @cfg {String} url The URL from which to request the data object.
8314      */
8315     /**
8316      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8317      */
8318     timeout : 30000,
8319     /**
8320      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8321      * the server the name of the callback function set up by the load call to process the returned data object.
8322      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8323      * javascript output which calls this named function passing the data object as its only parameter.
8324      */
8325     callbackParam : "callback",
8326     /**
8327      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8328      * name to the request.
8329      */
8330     nocache : true,
8331
8332     /**
8333      * Load data from the configured URL, read the data object into
8334      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8335      * process that block using the passed callback.
8336      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8337      * for the request to the remote server.
8338      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8339      * object into a block of Roo.data.Records.
8340      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8341      * The function must be passed <ul>
8342      * <li>The Record block object</li>
8343      * <li>The "arg" argument from the load function</li>
8344      * <li>A boolean success indicator</li>
8345      * </ul>
8346      * @param {Object} scope The scope in which to call the callback
8347      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8348      */
8349     load : function(params, reader, callback, scope, arg){
8350         if(this.fireEvent("beforeload", this, params) !== false){
8351
8352             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8353
8354             var url = this.url;
8355             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8356             if(this.nocache){
8357                 url += "&_dc=" + (new Date().getTime());
8358             }
8359             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8360             var trans = {
8361                 id : transId,
8362                 cb : "stcCallback"+transId,
8363                 scriptId : "stcScript"+transId,
8364                 params : params,
8365                 arg : arg,
8366                 url : url,
8367                 callback : callback,
8368                 scope : scope,
8369                 reader : reader
8370             };
8371             var conn = this;
8372
8373             window[trans.cb] = function(o){
8374                 conn.handleResponse(o, trans);
8375             };
8376
8377             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8378
8379             if(this.autoAbort !== false){
8380                 this.abort();
8381             }
8382
8383             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8384
8385             var script = document.createElement("script");
8386             script.setAttribute("src", url);
8387             script.setAttribute("type", "text/javascript");
8388             script.setAttribute("id", trans.scriptId);
8389             this.head.appendChild(script);
8390
8391             this.trans = trans;
8392         }else{
8393             callback.call(scope||this, null, arg, false);
8394         }
8395     },
8396
8397     // private
8398     isLoading : function(){
8399         return this.trans ? true : false;
8400     },
8401
8402     /**
8403      * Abort the current server request.
8404      */
8405     abort : function(){
8406         if(this.isLoading()){
8407             this.destroyTrans(this.trans);
8408         }
8409     },
8410
8411     // private
8412     destroyTrans : function(trans, isLoaded){
8413         this.head.removeChild(document.getElementById(trans.scriptId));
8414         clearTimeout(trans.timeoutId);
8415         if(isLoaded){
8416             window[trans.cb] = undefined;
8417             try{
8418                 delete window[trans.cb];
8419             }catch(e){}
8420         }else{
8421             // if hasn't been loaded, wait for load to remove it to prevent script error
8422             window[trans.cb] = function(){
8423                 window[trans.cb] = undefined;
8424                 try{
8425                     delete window[trans.cb];
8426                 }catch(e){}
8427             };
8428         }
8429     },
8430
8431     // private
8432     handleResponse : function(o, trans){
8433         this.trans = false;
8434         this.destroyTrans(trans, true);
8435         var result;
8436         try {
8437             result = trans.reader.readRecords(o);
8438         }catch(e){
8439             this.fireEvent("loadexception", this, o, trans.arg, e);
8440             trans.callback.call(trans.scope||window, null, trans.arg, false);
8441             return;
8442         }
8443         this.fireEvent("load", this, o, trans.arg);
8444         trans.callback.call(trans.scope||window, result, trans.arg, true);
8445     },
8446
8447     // private
8448     handleFailure : function(trans){
8449         this.trans = false;
8450         this.destroyTrans(trans, false);
8451         this.fireEvent("loadexception", this, null, trans.arg);
8452         trans.callback.call(trans.scope||window, null, trans.arg, false);
8453     }
8454 });/*
8455  * Based on:
8456  * Ext JS Library 1.1.1
8457  * Copyright(c) 2006-2007, Ext JS, LLC.
8458  *
8459  * Originally Released Under LGPL - original licence link has changed is not relivant.
8460  *
8461  * Fork - LGPL
8462  * <script type="text/javascript">
8463  */
8464
8465 /**
8466  * @class Roo.data.JsonReader
8467  * @extends Roo.data.DataReader
8468  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8469  * based on mappings in a provided Roo.data.Record constructor.
8470  * 
8471  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8472  * in the reply previously. 
8473  * 
8474  * <p>
8475  * Example code:
8476  * <pre><code>
8477 var RecordDef = Roo.data.Record.create([
8478     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8479     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8480 ]);
8481 var myReader = new Roo.data.JsonReader({
8482     totalProperty: "results",    // The property which contains the total dataset size (optional)
8483     root: "rows",                // The property which contains an Array of row objects
8484     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8485 }, RecordDef);
8486 </code></pre>
8487  * <p>
8488  * This would consume a JSON file like this:
8489  * <pre><code>
8490 { 'results': 2, 'rows': [
8491     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8492     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8493 }
8494 </code></pre>
8495  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8496  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8497  * paged from the remote server.
8498  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8499  * @cfg {String} root name of the property which contains the Array of row objects.
8500  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8501  * @constructor
8502  * Create a new JsonReader
8503  * @param {Object} meta Metadata configuration options
8504  * @param {Object} recordType Either an Array of field definition objects,
8505  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8506  */
8507 Roo.data.JsonReader = function(meta, recordType){
8508     
8509     meta = meta || {};
8510     // set some defaults:
8511     Roo.applyIf(meta, {
8512         totalProperty: 'total',
8513         successProperty : 'success',
8514         root : 'data',
8515         id : 'id'
8516     });
8517     
8518     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8519 };
8520 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8521     
8522     /**
8523      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8524      * Used by Store query builder to append _requestMeta to params.
8525      * 
8526      */
8527     metaFromRemote : false,
8528     /**
8529      * This method is only used by a DataProxy which has retrieved data from a remote server.
8530      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8531      * @return {Object} data A data block which is used by an Roo.data.Store object as
8532      * a cache of Roo.data.Records.
8533      */
8534     read : function(response){
8535         var json = response.responseText;
8536        
8537         var o = /* eval:var:o */ eval("("+json+")");
8538         if(!o) {
8539             throw {message: "JsonReader.read: Json object not found"};
8540         }
8541         
8542         if(o.metaData){
8543             
8544             delete this.ef;
8545             this.metaFromRemote = true;
8546             this.meta = o.metaData;
8547             this.recordType = Roo.data.Record.create(o.metaData.fields);
8548             this.onMetaChange(this.meta, this.recordType, o);
8549         }
8550         return this.readRecords(o);
8551     },
8552
8553     // private function a store will implement
8554     onMetaChange : function(meta, recordType, o){
8555
8556     },
8557
8558     /**
8559          * @ignore
8560          */
8561     simpleAccess: function(obj, subsc) {
8562         return obj[subsc];
8563     },
8564
8565         /**
8566          * @ignore
8567          */
8568     getJsonAccessor: function(){
8569         var re = /[\[\.]/;
8570         return function(expr) {
8571             try {
8572                 return(re.test(expr))
8573                     ? new Function("obj", "return obj." + expr)
8574                     : function(obj){
8575                         return obj[expr];
8576                     };
8577             } catch(e){}
8578             return Roo.emptyFn;
8579         };
8580     }(),
8581
8582     /**
8583      * Create a data block containing Roo.data.Records from an XML document.
8584      * @param {Object} o An object which contains an Array of row objects in the property specified
8585      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8586      * which contains the total size of the dataset.
8587      * @return {Object} data A data block which is used by an Roo.data.Store object as
8588      * a cache of Roo.data.Records.
8589      */
8590     readRecords : function(o){
8591         /**
8592          * After any data loads, the raw JSON data is available for further custom processing.
8593          * @type Object
8594          */
8595         this.o = o;
8596         var s = this.meta, Record = this.recordType,
8597             f = Record.prototype.fields, fi = f.items, fl = f.length;
8598
8599 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8600         if (!this.ef) {
8601             if(s.totalProperty) {
8602                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8603                 }
8604                 if(s.successProperty) {
8605                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8606                 }
8607                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8608                 if (s.id) {
8609                         var g = this.getJsonAccessor(s.id);
8610                         this.getId = function(rec) {
8611                                 var r = g(rec);
8612                                 return (r === undefined || r === "") ? null : r;
8613                         };
8614                 } else {
8615                         this.getId = function(){return null;};
8616                 }
8617             this.ef = [];
8618             for(var jj = 0; jj < fl; jj++){
8619                 f = fi[jj];
8620                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8621                 this.ef[jj] = this.getJsonAccessor(map);
8622             }
8623         }
8624
8625         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8626         if(s.totalProperty){
8627             var vt = parseInt(this.getTotal(o), 10);
8628             if(!isNaN(vt)){
8629                 totalRecords = vt;
8630             }
8631         }
8632         if(s.successProperty){
8633             var vs = this.getSuccess(o);
8634             if(vs === false || vs === 'false'){
8635                 success = false;
8636             }
8637         }
8638         var records = [];
8639             for(var i = 0; i < c; i++){
8640                     var n = root[i];
8641                 var values = {};
8642                 var id = this.getId(n);
8643                 for(var j = 0; j < fl; j++){
8644                     f = fi[j];
8645                 var v = this.ef[j](n);
8646                 if (!f.convert) {
8647                     Roo.log('missing convert for ' + f.name);
8648                     Roo.log(f);
8649                     continue;
8650                 }
8651                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8652                 }
8653                 var record = new Record(values, id);
8654                 record.json = n;
8655                 records[i] = record;
8656             }
8657             return {
8658             raw : o,
8659                 success : success,
8660                 records : records,
8661                 totalRecords : totalRecords
8662             };
8663     }
8664 });/*
8665  * Based on:
8666  * Ext JS Library 1.1.1
8667  * Copyright(c) 2006-2007, Ext JS, LLC.
8668  *
8669  * Originally Released Under LGPL - original licence link has changed is not relivant.
8670  *
8671  * Fork - LGPL
8672  * <script type="text/javascript">
8673  */
8674
8675 /**
8676  * @class Roo.data.ArrayReader
8677  * @extends Roo.data.DataReader
8678  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8679  * Each element of that Array represents a row of data fields. The
8680  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8681  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8682  * <p>
8683  * Example code:.
8684  * <pre><code>
8685 var RecordDef = Roo.data.Record.create([
8686     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8687     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8688 ]);
8689 var myReader = new Roo.data.ArrayReader({
8690     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8691 }, RecordDef);
8692 </code></pre>
8693  * <p>
8694  * This would consume an Array like this:
8695  * <pre><code>
8696 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8697   </code></pre>
8698  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8699  * @constructor
8700  * Create a new JsonReader
8701  * @param {Object} meta Metadata configuration options.
8702  * @param {Object} recordType Either an Array of field definition objects
8703  * as specified to {@link Roo.data.Record#create},
8704  * or an {@link Roo.data.Record} object
8705  * created using {@link Roo.data.Record#create}.
8706  */
8707 Roo.data.ArrayReader = function(meta, recordType){
8708     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8709 };
8710
8711 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8712     /**
8713      * Create a data block containing Roo.data.Records from an XML document.
8714      * @param {Object} o An Array of row objects which represents the dataset.
8715      * @return {Object} data A data block which is used by an Roo.data.Store object as
8716      * a cache of Roo.data.Records.
8717      */
8718     readRecords : function(o){
8719         var sid = this.meta ? this.meta.id : null;
8720         var recordType = this.recordType, fields = recordType.prototype.fields;
8721         var records = [];
8722         var root = o;
8723             for(var i = 0; i < root.length; i++){
8724                     var n = root[i];
8725                 var values = {};
8726                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8727                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8728                 var f = fields.items[j];
8729                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8730                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8731                 v = f.convert(v);
8732                 values[f.name] = v;
8733             }
8734                 var record = new recordType(values, id);
8735                 record.json = n;
8736                 records[records.length] = record;
8737             }
8738             return {
8739                 records : records,
8740                 totalRecords : records.length
8741             };
8742     }
8743 });/*
8744  * - LGPL
8745  * * 
8746  */
8747
8748 /**
8749  * @class Roo.bootstrap.ComboBox
8750  * @extends Roo.bootstrap.TriggerField
8751  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8752  * @cfg {Boolean} append (true|false) default false
8753  * @constructor
8754  * Create a new ComboBox.
8755  * @param {Object} config Configuration options
8756  */
8757 Roo.bootstrap.ComboBox = function(config){
8758     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8759     this.addEvents({
8760         /**
8761          * @event expand
8762          * Fires when the dropdown list is expanded
8763              * @param {Roo.bootstrap.ComboBox} combo This combo box
8764              */
8765         'expand' : true,
8766         /**
8767          * @event collapse
8768          * Fires when the dropdown list is collapsed
8769              * @param {Roo.bootstrap.ComboBox} combo This combo box
8770              */
8771         'collapse' : true,
8772         /**
8773          * @event beforeselect
8774          * Fires before a list item is selected. Return false to cancel the selection.
8775              * @param {Roo.bootstrap.ComboBox} combo This combo box
8776              * @param {Roo.data.Record} record The data record returned from the underlying store
8777              * @param {Number} index The index of the selected item in the dropdown list
8778              */
8779         'beforeselect' : true,
8780         /**
8781          * @event select
8782          * Fires when a list item is selected
8783              * @param {Roo.bootstrap.ComboBox} combo This combo box
8784              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8785              * @param {Number} index The index of the selected item in the dropdown list
8786              */
8787         'select' : true,
8788         /**
8789          * @event beforequery
8790          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8791          * The event object passed has these properties:
8792              * @param {Roo.bootstrap.ComboBox} combo This combo box
8793              * @param {String} query The query
8794              * @param {Boolean} forceAll true to force "all" query
8795              * @param {Boolean} cancel true to cancel the query
8796              * @param {Object} e The query event object
8797              */
8798         'beforequery': true,
8799          /**
8800          * @event add
8801          * Fires when the 'add' icon is pressed (add a listener to enable add button)
8802              * @param {Roo.bootstrap.ComboBox} combo This combo box
8803              */
8804         'add' : true,
8805         /**
8806          * @event edit
8807          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
8808              * @param {Roo.bootstrap.ComboBox} combo This combo box
8809              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
8810              */
8811         'edit' : true,
8812         /**
8813          * @event remove
8814          * Fires when the remove value from the combobox array
8815              * @param {Roo.bootstrap.ComboBox} combo This combo box
8816              */
8817         'remove' : true
8818         
8819     });
8820     
8821     
8822     this.selectedIndex = -1;
8823     if(this.mode == 'local'){
8824         if(config.queryDelay === undefined){
8825             this.queryDelay = 10;
8826         }
8827         if(config.minChars === undefined){
8828             this.minChars = 0;
8829         }
8830     }
8831 };
8832
8833 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
8834      
8835     /**
8836      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
8837      * rendering into an Roo.Editor, defaults to false)
8838      */
8839     /**
8840      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
8841      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
8842      */
8843     /**
8844      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
8845      */
8846     /**
8847      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
8848      * the dropdown list (defaults to undefined, with no header element)
8849      */
8850
8851      /**
8852      * @cfg {String/Roo.Template} tpl The template to use to render the output
8853      */
8854      
8855      /**
8856      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
8857      */
8858     listWidth: undefined,
8859     /**
8860      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8861      * mode = 'remote' or 'text' if mode = 'local')
8862      */
8863     displayField: undefined,
8864     /**
8865      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8866      * mode = 'remote' or 'value' if mode = 'local'). 
8867      * Note: use of a valueField requires the user make a selection
8868      * in order for a value to be mapped.
8869      */
8870     valueField: undefined,
8871     
8872     
8873     /**
8874      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8875      * field's data value (defaults to the underlying DOM element's name)
8876      */
8877     hiddenName: undefined,
8878     /**
8879      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8880      */
8881     listClass: '',
8882     /**
8883      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8884      */
8885     selectedClass: 'active',
8886     
8887     /**
8888      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8889      */
8890     shadow:'sides',
8891     /**
8892      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8893      * anchor positions (defaults to 'tl-bl')
8894      */
8895     listAlign: 'tl-bl?',
8896     /**
8897      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8898      */
8899     maxHeight: 300,
8900     /**
8901      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
8902      * query specified by the allQuery config option (defaults to 'query')
8903      */
8904     triggerAction: 'query',
8905     /**
8906      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8907      * (defaults to 4, does not apply if editable = false)
8908      */
8909     minChars : 4,
8910     /**
8911      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8912      * delay (typeAheadDelay) if it matches a known value (defaults to false)
8913      */
8914     typeAhead: false,
8915     /**
8916      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8917      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8918      */
8919     queryDelay: 500,
8920     /**
8921      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8922      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
8923      */
8924     pageSize: 0,
8925     /**
8926      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
8927      * when editable = true (defaults to false)
8928      */
8929     selectOnFocus:false,
8930     /**
8931      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8932      */
8933     queryParam: 'query',
8934     /**
8935      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
8936      * when mode = 'remote' (defaults to 'Loading...')
8937      */
8938     loadingText: 'Loading...',
8939     /**
8940      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8941      */
8942     resizable: false,
8943     /**
8944      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8945      */
8946     handleHeight : 8,
8947     /**
8948      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8949      * traditional select (defaults to true)
8950      */
8951     editable: true,
8952     /**
8953      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8954      */
8955     allQuery: '',
8956     /**
8957      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8958      */
8959     mode: 'remote',
8960     /**
8961      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8962      * listWidth has a higher value)
8963      */
8964     minListWidth : 70,
8965     /**
8966      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8967      * allow the user to set arbitrary text into the field (defaults to false)
8968      */
8969     forceSelection:false,
8970     /**
8971      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8972      * if typeAhead = true (defaults to 250)
8973      */
8974     typeAheadDelay : 250,
8975     /**
8976      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8977      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8978      */
8979     valueNotFoundText : undefined,
8980     /**
8981      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8982      */
8983     blockFocus : false,
8984     
8985     /**
8986      * @cfg {Boolean} disableClear Disable showing of clear button.
8987      */
8988     disableClear : false,
8989     /**
8990      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8991      */
8992     alwaysQuery : false,
8993     
8994     /**
8995      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8996      */
8997     multiple : false,
8998     
8999     //private
9000     addicon : false,
9001     editicon: false,
9002     
9003     page: 0,
9004     hasQuery: false,
9005     append: false,
9006     loadNext: false,
9007     item: [],
9008     
9009     // element that contains real text value.. (when hidden is used..)
9010      
9011     // private
9012     initEvents: function(){
9013         
9014         if (!this.store) {
9015             throw "can not find store for combo";
9016         }
9017         this.store = Roo.factory(this.store, Roo.data);
9018         
9019         
9020         
9021         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9022         
9023         
9024         if(this.hiddenName){
9025             
9026             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9027             
9028             this.hiddenField.dom.value =
9029                 this.hiddenValue !== undefined ? this.hiddenValue :
9030                 this.value !== undefined ? this.value : '';
9031
9032             // prevent input submission
9033             this.el.dom.removeAttribute('name');
9034             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9035              
9036              
9037         }
9038         //if(Roo.isGecko){
9039         //    this.el.dom.setAttribute('autocomplete', 'off');
9040         //}
9041
9042         var cls = 'x-combo-list';
9043         this.list = this.el.select('ul.dropdown-menu',true).first();
9044
9045         //this.list = new Roo.Layer({
9046         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9047         //});
9048         
9049         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9050         this.list.setWidth(lw);
9051         
9052         this.list.on('mouseover', this.onViewOver, this);
9053         this.list.on('mousemove', this.onViewMove, this);
9054         
9055         this.list.on('scroll', this.onViewScroll, this);
9056         
9057         /*
9058         this.list.swallowEvent('mousewheel');
9059         this.assetHeight = 0;
9060
9061         if(this.title){
9062             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9063             this.assetHeight += this.header.getHeight();
9064         }
9065
9066         this.innerList = this.list.createChild({cls:cls+'-inner'});
9067         this.innerList.on('mouseover', this.onViewOver, this);
9068         this.innerList.on('mousemove', this.onViewMove, this);
9069         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9070         
9071         if(this.allowBlank && !this.pageSize && !this.disableClear){
9072             this.footer = this.list.createChild({cls:cls+'-ft'});
9073             this.pageTb = new Roo.Toolbar(this.footer);
9074            
9075         }
9076         if(this.pageSize){
9077             this.footer = this.list.createChild({cls:cls+'-ft'});
9078             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9079                     {pageSize: this.pageSize});
9080             
9081         }
9082         
9083         if (this.pageTb && this.allowBlank && !this.disableClear) {
9084             var _this = this;
9085             this.pageTb.add(new Roo.Toolbar.Fill(), {
9086                 cls: 'x-btn-icon x-btn-clear',
9087                 text: '&#160;',
9088                 handler: function()
9089                 {
9090                     _this.collapse();
9091                     _this.clearValue();
9092                     _this.onSelect(false, -1);
9093                 }
9094             });
9095         }
9096         if (this.footer) {
9097             this.assetHeight += this.footer.getHeight();
9098         }
9099         */
9100             
9101         if(!this.tpl){
9102             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9103         }
9104
9105         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9106             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9107         });
9108         //this.view.wrapEl.setDisplayed(false);
9109         this.view.on('click', this.onViewClick, this);
9110         
9111         
9112         
9113         this.store.on('beforeload', this.onBeforeLoad, this);
9114         this.store.on('load', this.onLoad, this);
9115         this.store.on('loadexception', this.onLoadException, this);
9116         /*
9117         if(this.resizable){
9118             this.resizer = new Roo.Resizable(this.list,  {
9119                pinned:true, handles:'se'
9120             });
9121             this.resizer.on('resize', function(r, w, h){
9122                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9123                 this.listWidth = w;
9124                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9125                 this.restrictHeight();
9126             }, this);
9127             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9128         }
9129         */
9130         if(!this.editable){
9131             this.editable = true;
9132             this.setEditable(false);
9133         }
9134         
9135         /*
9136         
9137         if (typeof(this.events.add.listeners) != 'undefined') {
9138             
9139             this.addicon = this.wrap.createChild(
9140                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9141        
9142             this.addicon.on('click', function(e) {
9143                 this.fireEvent('add', this);
9144             }, this);
9145         }
9146         if (typeof(this.events.edit.listeners) != 'undefined') {
9147             
9148             this.editicon = this.wrap.createChild(
9149                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9150             if (this.addicon) {
9151                 this.editicon.setStyle('margin-left', '40px');
9152             }
9153             this.editicon.on('click', function(e) {
9154                 
9155                 // we fire even  if inothing is selected..
9156                 this.fireEvent('edit', this, this.lastData );
9157                 
9158             }, this);
9159         }
9160         */
9161         
9162         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9163             "up" : function(e){
9164                 this.inKeyMode = true;
9165                 this.selectPrev();
9166             },
9167
9168             "down" : function(e){
9169                 if(!this.isExpanded()){
9170                     this.onTriggerClick();
9171                 }else{
9172                     this.inKeyMode = true;
9173                     this.selectNext();
9174                 }
9175             },
9176
9177             "enter" : function(e){
9178                 this.onViewClick();
9179                 //return true;
9180             },
9181
9182             "esc" : function(e){
9183                 this.collapse();
9184             },
9185
9186             "tab" : function(e){
9187                 this.collapse();
9188                 
9189                 if(this.fireEvent("specialkey", this, e)){
9190                     this.onViewClick(false);
9191                 }
9192                 
9193                 return true;
9194             },
9195
9196             scope : this,
9197
9198             doRelay : function(foo, bar, hname){
9199                 if(hname == 'down' || this.scope.isExpanded()){
9200                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9201                 }
9202                 return true;
9203             },
9204
9205             forceKeyDown: true
9206         });
9207         
9208         
9209         this.queryDelay = Math.max(this.queryDelay || 10,
9210                 this.mode == 'local' ? 10 : 250);
9211         
9212         
9213         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9214         
9215         if(this.typeAhead){
9216             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9217         }
9218         if(this.editable !== false){
9219             this.inputEl().on("keyup", this.onKeyUp, this);
9220         }
9221         if(this.forceSelection){
9222             this.on('blur', this.doForce, this);
9223         }
9224         
9225         if(this.multiple){
9226             this.choices = this.el.select('ul.select2-choices', true).first();
9227             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9228         }
9229     },
9230
9231     onDestroy : function(){
9232         if(this.view){
9233             this.view.setStore(null);
9234             this.view.el.removeAllListeners();
9235             this.view.el.remove();
9236             this.view.purgeListeners();
9237         }
9238         if(this.list){
9239             this.list.dom.innerHTML  = '';
9240         }
9241         if(this.store){
9242             this.store.un('beforeload', this.onBeforeLoad, this);
9243             this.store.un('load', this.onLoad, this);
9244             this.store.un('loadexception', this.onLoadException, this);
9245         }
9246         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9247     },
9248
9249     // private
9250     fireKey : function(e){
9251         if(e.isNavKeyPress() && !this.list.isVisible()){
9252             this.fireEvent("specialkey", this, e);
9253         }
9254     },
9255
9256     // private
9257     onResize: function(w, h){
9258 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9259 //        
9260 //        if(typeof w != 'number'){
9261 //            // we do not handle it!?!?
9262 //            return;
9263 //        }
9264 //        var tw = this.trigger.getWidth();
9265 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9266 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9267 //        var x = w - tw;
9268 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9269 //            
9270 //        //this.trigger.setStyle('left', x+'px');
9271 //        
9272 //        if(this.list && this.listWidth === undefined){
9273 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9274 //            this.list.setWidth(lw);
9275 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9276 //        }
9277         
9278     
9279         
9280     },
9281
9282     /**
9283      * Allow or prevent the user from directly editing the field text.  If false is passed,
9284      * the user will only be able to select from the items defined in the dropdown list.  This method
9285      * is the runtime equivalent of setting the 'editable' config option at config time.
9286      * @param {Boolean} value True to allow the user to directly edit the field text
9287      */
9288     setEditable : function(value){
9289         if(value == this.editable){
9290             return;
9291         }
9292         this.editable = value;
9293         if(!value){
9294             this.inputEl().dom.setAttribute('readOnly', true);
9295             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9296             this.inputEl().addClass('x-combo-noedit');
9297         }else{
9298             this.inputEl().dom.setAttribute('readOnly', false);
9299             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9300             this.inputEl().removeClass('x-combo-noedit');
9301         }
9302     },
9303
9304     // private
9305     
9306     onBeforeLoad : function(combo,opts){
9307         if(!this.hasFocus){
9308             return;
9309         }
9310          if (!opts.add) {
9311             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9312          }
9313         this.restrictHeight();
9314         this.selectedIndex = -1;
9315     },
9316
9317     // private
9318     onLoad : function(){
9319         
9320         this.hasQuery = false;
9321         
9322         if(!this.hasFocus){
9323             return;
9324         }
9325         
9326         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9327             this.loading.hide();
9328         }
9329         
9330         if(this.store.getCount() > 0){
9331             this.expand();
9332             this.restrictHeight();
9333             if(this.lastQuery == this.allQuery){
9334                 if(this.editable){
9335                     this.inputEl().dom.select();
9336                 }
9337                 if(!this.selectByValue(this.value, true)){
9338                     this.select(0, true);
9339                 }
9340             }else{
9341                 this.selectNext();
9342                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9343                     this.taTask.delay(this.typeAheadDelay);
9344                 }
9345             }
9346         }else{
9347             this.onEmptyResults();
9348         }
9349         
9350         //this.el.focus();
9351     },
9352     // private
9353     onLoadException : function()
9354     {
9355         this.hasQuery = false;
9356         
9357         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9358             this.loading.hide();
9359         }
9360         
9361         this.collapse();
9362         Roo.log(this.store.reader.jsonData);
9363         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9364             // fixme
9365             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9366         }
9367         
9368         
9369     },
9370     // private
9371     onTypeAhead : function(){
9372         if(this.store.getCount() > 0){
9373             var r = this.store.getAt(0);
9374             var newValue = r.data[this.displayField];
9375             var len = newValue.length;
9376             var selStart = this.getRawValue().length;
9377             
9378             if(selStart != len){
9379                 this.setRawValue(newValue);
9380                 this.selectText(selStart, newValue.length);
9381             }
9382         }
9383     },
9384
9385     // private
9386     onSelect : function(record, index){
9387         
9388         if(this.fireEvent('beforeselect', this, record, index) !== false){
9389         
9390             this.setFromData(index > -1 ? record.data : false);
9391             
9392             this.collapse();
9393             this.fireEvent('select', this, record, index);
9394         }
9395     },
9396
9397     /**
9398      * Returns the currently selected field value or empty string if no value is set.
9399      * @return {String} value The selected value
9400      */
9401     getValue : function(){
9402         
9403         if(this.multiple){
9404             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9405         }
9406         
9407         if(this.valueField){
9408             return typeof this.value != 'undefined' ? this.value : '';
9409         }else{
9410             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9411         }
9412     },
9413
9414     /**
9415      * Clears any text/value currently set in the field
9416      */
9417     clearValue : function(){
9418         if(this.hiddenField){
9419             this.hiddenField.dom.value = '';
9420         }
9421         this.value = '';
9422         this.setRawValue('');
9423         this.lastSelectionText = '';
9424         
9425     },
9426
9427     /**
9428      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9429      * will be displayed in the field.  If the value does not match the data value of an existing item,
9430      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9431      * Otherwise the field will be blank (although the value will still be set).
9432      * @param {String} value The value to match
9433      */
9434     setValue : function(v){
9435         if(this.multiple){
9436             this.syncValue();
9437             return;
9438         }
9439         
9440         var text = v;
9441         if(this.valueField){
9442             var r = this.findRecord(this.valueField, v);
9443             if(r){
9444                 text = r.data[this.displayField];
9445             }else if(this.valueNotFoundText !== undefined){
9446                 text = this.valueNotFoundText;
9447             }
9448         }
9449         this.lastSelectionText = text;
9450         if(this.hiddenField){
9451             this.hiddenField.dom.value = v;
9452         }
9453         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9454         this.value = v;
9455     },
9456     /**
9457      * @property {Object} the last set data for the element
9458      */
9459     
9460     lastData : false,
9461     /**
9462      * Sets the value of the field based on a object which is related to the record format for the store.
9463      * @param {Object} value the value to set as. or false on reset?
9464      */
9465     setFromData : function(o){
9466         
9467         if(this.multiple){
9468             this.addItem(o);
9469             return;
9470         }
9471             
9472         var dv = ''; // display value
9473         var vv = ''; // value value..
9474         this.lastData = o;
9475         if (this.displayField) {
9476             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9477         } else {
9478             // this is an error condition!!!
9479             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9480         }
9481         
9482         if(this.valueField){
9483             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9484         }
9485         
9486         if(this.hiddenField){
9487             this.hiddenField.dom.value = vv;
9488             
9489             this.lastSelectionText = dv;
9490             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9491             this.value = vv;
9492             return;
9493         }
9494         // no hidden field.. - we store the value in 'value', but still display
9495         // display field!!!!
9496         this.lastSelectionText = dv;
9497         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9498         this.value = vv;
9499         
9500         
9501     },
9502     // private
9503     reset : function(){
9504         // overridden so that last data is reset..
9505         this.setValue(this.originalValue);
9506         this.clearInvalid();
9507         this.lastData = false;
9508         if (this.view) {
9509             this.view.clearSelections();
9510         }
9511     },
9512     // private
9513     findRecord : function(prop, value){
9514         var record;
9515         if(this.store.getCount() > 0){
9516             this.store.each(function(r){
9517                 if(r.data[prop] == value){
9518                     record = r;
9519                     return false;
9520                 }
9521                 return true;
9522             });
9523         }
9524         return record;
9525     },
9526     
9527     getName: function()
9528     {
9529         // returns hidden if it's set..
9530         if (!this.rendered) {return ''};
9531         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9532         
9533     },
9534     // private
9535     onViewMove : function(e, t){
9536         this.inKeyMode = false;
9537     },
9538
9539     // private
9540     onViewOver : function(e, t){
9541         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9542             return;
9543         }
9544         var item = this.view.findItemFromChild(t);
9545         if(item){
9546             var index = this.view.indexOf(item);
9547             this.select(index, false);
9548         }
9549     },
9550
9551     // private
9552     onViewClick : function(doFocus)
9553     {
9554         var index = this.view.getSelectedIndexes()[0];
9555         var r = this.store.getAt(index);
9556         if(r){
9557             this.onSelect(r, index);
9558         }
9559         if(doFocus !== false && !this.blockFocus){
9560             this.inputEl().focus();
9561         }
9562     },
9563
9564     // private
9565     restrictHeight : function(){
9566         //this.innerList.dom.style.height = '';
9567         //var inner = this.innerList.dom;
9568         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9569         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9570         //this.list.beginUpdate();
9571         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9572         this.list.alignTo(this.inputEl(), this.listAlign);
9573         //this.list.endUpdate();
9574     },
9575
9576     // private
9577     onEmptyResults : function(){
9578         this.collapse();
9579     },
9580
9581     /**
9582      * Returns true if the dropdown list is expanded, else false.
9583      */
9584     isExpanded : function(){
9585         return this.list.isVisible();
9586     },
9587
9588     /**
9589      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9590      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9591      * @param {String} value The data value of the item to select
9592      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9593      * selected item if it is not currently in view (defaults to true)
9594      * @return {Boolean} True if the value matched an item in the list, else false
9595      */
9596     selectByValue : function(v, scrollIntoView){
9597         if(v !== undefined && v !== null){
9598             var r = this.findRecord(this.valueField || this.displayField, v);
9599             if(r){
9600                 this.select(this.store.indexOf(r), scrollIntoView);
9601                 return true;
9602             }
9603         }
9604         return false;
9605     },
9606
9607     /**
9608      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9609      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9610      * @param {Number} index The zero-based index of the list item to select
9611      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9612      * selected item if it is not currently in view (defaults to true)
9613      */
9614     select : function(index, scrollIntoView){
9615         this.selectedIndex = index;
9616         this.view.select(index);
9617         if(scrollIntoView !== false){
9618             var el = this.view.getNode(index);
9619             if(el){
9620                 //this.innerList.scrollChildIntoView(el, false);
9621                 
9622             }
9623         }
9624     },
9625
9626     // private
9627     selectNext : function(){
9628         var ct = this.store.getCount();
9629         if(ct > 0){
9630             if(this.selectedIndex == -1){
9631                 this.select(0);
9632             }else if(this.selectedIndex < ct-1){
9633                 this.select(this.selectedIndex+1);
9634             }
9635         }
9636     },
9637
9638     // private
9639     selectPrev : function(){
9640         var ct = this.store.getCount();
9641         if(ct > 0){
9642             if(this.selectedIndex == -1){
9643                 this.select(0);
9644             }else if(this.selectedIndex != 0){
9645                 this.select(this.selectedIndex-1);
9646             }
9647         }
9648     },
9649
9650     // private
9651     onKeyUp : function(e){
9652         if(this.editable !== false && !e.isSpecialKey()){
9653             this.lastKey = e.getKey();
9654             this.dqTask.delay(this.queryDelay);
9655         }
9656     },
9657
9658     // private
9659     validateBlur : function(){
9660         return !this.list || !this.list.isVisible();   
9661     },
9662
9663     // private
9664     initQuery : function(){
9665         this.doQuery(this.getRawValue());
9666     },
9667
9668     // private
9669     doForce : function(){
9670         if(this.el.dom.value.length > 0){
9671             this.el.dom.value =
9672                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9673              
9674         }
9675     },
9676
9677     /**
9678      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9679      * query allowing the query action to be canceled if needed.
9680      * @param {String} query The SQL query to execute
9681      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9682      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9683      * saved in the current store (defaults to false)
9684      */
9685     doQuery : function(q, forceAll){
9686         
9687         if(q === undefined || q === null){
9688             q = '';
9689         }
9690         var qe = {
9691             query: q,
9692             forceAll: forceAll,
9693             combo: this,
9694             cancel:false
9695         };
9696         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9697             return false;
9698         }
9699         q = qe.query;
9700         
9701         forceAll = qe.forceAll;
9702         if(forceAll === true || (q.length >= this.minChars)){
9703             
9704             this.hasQuery = true;
9705             
9706             if(this.lastQuery != q || this.alwaysQuery){
9707                 this.lastQuery = q;
9708                 if(this.mode == 'local'){
9709                     this.selectedIndex = -1;
9710                     if(forceAll){
9711                         this.store.clearFilter();
9712                     }else{
9713                         this.store.filter(this.displayField, q);
9714                     }
9715                     this.onLoad();
9716                 }else{
9717                     this.store.baseParams[this.queryParam] = q;
9718                     
9719                     var options = {params : this.getParams(q)};
9720                     
9721                     if(this.loadNext){
9722                         options.add = true;
9723                         options.params.start = this.page * this.pageSize;
9724                     }
9725                     
9726                     this.store.load(options);
9727                     this.expand();
9728                 }
9729             }else{
9730                 this.selectedIndex = -1;
9731                 this.onLoad();   
9732             }
9733         }
9734         
9735         this.loadNext = false;
9736     },
9737
9738     // private
9739     getParams : function(q){
9740         var p = {};
9741         //p[this.queryParam] = q;
9742         
9743         if(this.pageSize){
9744             p.start = 0;
9745             p.limit = this.pageSize;
9746         }
9747         return p;
9748     },
9749
9750     /**
9751      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9752      */
9753     collapse : function(){
9754         if(!this.isExpanded()){
9755             return;
9756         }
9757         
9758         this.list.hide();
9759         Roo.get(document).un('mousedown', this.collapseIf, this);
9760         Roo.get(document).un('mousewheel', this.collapseIf, this);
9761         if (!this.editable) {
9762             Roo.get(document).un('keydown', this.listKeyPress, this);
9763         }
9764         this.fireEvent('collapse', this);
9765     },
9766
9767     // private
9768     collapseIf : function(e){
9769         var in_combo  = e.within(this.el);
9770         var in_list =  e.within(this.list);
9771         
9772         if (in_combo || in_list) {
9773             //e.stopPropagation();
9774             return;
9775         }
9776
9777         this.collapse();
9778         
9779     },
9780
9781     /**
9782      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9783      */
9784     expand : function(){
9785        
9786         if(this.isExpanded() || !this.hasFocus){
9787             return;
9788         }
9789          Roo.log('expand');
9790         this.list.alignTo(this.inputEl(), this.listAlign);
9791         this.list.show();
9792         Roo.get(document).on('mousedown', this.collapseIf, this);
9793         Roo.get(document).on('mousewheel', this.collapseIf, this);
9794         if (!this.editable) {
9795             Roo.get(document).on('keydown', this.listKeyPress, this);
9796         }
9797         
9798         this.fireEvent('expand', this);
9799     },
9800
9801     // private
9802     // Implements the default empty TriggerField.onTriggerClick function
9803     onTriggerClick : function()
9804     {
9805         Roo.log('trigger click');
9806         
9807         if(this.disabled){
9808             return;
9809         }
9810         
9811         this.page = 0;
9812         this.loadNext = false;
9813         
9814         if(this.isExpanded()){
9815             this.collapse();
9816             if (!this.blockFocus) {
9817                 this.inputEl().focus();
9818             }
9819             
9820         }else {
9821             this.hasFocus = true;
9822             if(this.triggerAction == 'all') {
9823                 this.doQuery(this.allQuery, true);
9824             } else {
9825                 this.doQuery(this.getRawValue());
9826             }
9827             if (!this.blockFocus) {
9828                 this.inputEl().focus();
9829             }
9830         }
9831     },
9832     listKeyPress : function(e)
9833     {
9834         //Roo.log('listkeypress');
9835         // scroll to first matching element based on key pres..
9836         if (e.isSpecialKey()) {
9837             return false;
9838         }
9839         var k = String.fromCharCode(e.getKey()).toUpperCase();
9840         //Roo.log(k);
9841         var match  = false;
9842         var csel = this.view.getSelectedNodes();
9843         var cselitem = false;
9844         if (csel.length) {
9845             var ix = this.view.indexOf(csel[0]);
9846             cselitem  = this.store.getAt(ix);
9847             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
9848                 cselitem = false;
9849             }
9850             
9851         }
9852         
9853         this.store.each(function(v) { 
9854             if (cselitem) {
9855                 // start at existing selection.
9856                 if (cselitem.id == v.id) {
9857                     cselitem = false;
9858                 }
9859                 return true;
9860             }
9861                 
9862             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9863                 match = this.store.indexOf(v);
9864                 return false;
9865             }
9866             return true;
9867         }, this);
9868         
9869         if (match === false) {
9870             return true; // no more action?
9871         }
9872         // scroll to?
9873         this.view.select(match);
9874         var sn = Roo.get(this.view.getSelectedNodes()[0])
9875         //sn.scrollIntoView(sn.dom.parentNode, false);
9876     },
9877     
9878     onViewScroll : function(e, t){
9879         
9880         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9881             return;
9882         }
9883         
9884         this.hasQuery = true;
9885         
9886         this.loading = this.list.select('.loading', true).first();
9887         
9888         if(this.loading === null){
9889             this.list.createChild({
9890                 tag: 'div',
9891                 cls: 'loading select2-more-results select2-active',
9892                 html: 'Loading more results...'
9893             })
9894             
9895             this.loading = this.list.select('.loading', true).first();
9896             
9897             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9898             
9899             this.loading.hide();
9900         }
9901         
9902         this.loading.show();
9903         
9904         var _combo = this;
9905         
9906         this.page++;
9907         this.loadNext = true;
9908         
9909         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9910         
9911         return;
9912     },
9913     
9914     addItem : function(o)
9915     {   
9916         var dv = ''; // display value
9917         
9918         if (this.displayField) {
9919             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9920         } else {
9921             // this is an error condition!!!
9922             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9923         }
9924         
9925         if(!dv.length){
9926             return;
9927         }
9928         
9929         var choice = this.choices.createChild({
9930             tag: 'li',
9931             cls: 'select2-search-choice',
9932             cn: [
9933                 {
9934                     tag: 'div',
9935                     html: dv
9936                 },
9937                 {
9938                     tag: 'a',
9939                     href: '#',
9940                     cls: 'select2-search-choice-close',
9941                     tabindex: '-1'
9942                 }
9943             ]
9944             
9945         }, this.searchField);
9946         
9947         var close = choice.select('a.select2-search-choice-close', true).first()
9948         
9949         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9950         
9951         this.item.push(o);
9952         this.lastData = o;
9953         
9954         this.syncValue();
9955         
9956         this.inputEl().dom.value = '';
9957         
9958     },
9959     
9960     onRemoveItem : function(e, _self, o)
9961     {
9962         Roo.log('remove item');
9963         var index = this.item.indexOf(o.data) * 1;
9964         
9965         if( index < 0){
9966             Roo.log('not this item?!');
9967             return;
9968         }
9969         
9970         this.item.splice(index, 1);
9971         o.item.remove();
9972         
9973         this.syncValue();
9974         
9975         this.fireEvent('remove', this);
9976         
9977     },
9978     
9979     syncValue : function()
9980     {
9981         if(!this.item.length){
9982             this.clearValue();
9983             return;
9984         }
9985             
9986         var value = [];
9987         var _this = this;
9988         Roo.each(this.item, function(i){
9989             if(_this.valueField){
9990                 value.push(i[_this.valueField]);
9991                 return;
9992             }
9993
9994             value.push(i);
9995         });
9996
9997         this.value = value.join(',');
9998
9999         if(this.hiddenField){
10000             this.hiddenField.dom.value = this.value;
10001         }
10002     },
10003     
10004     clearItem : function()
10005     {
10006         if(!this.multiple){
10007             return;
10008         }
10009         
10010         this.item = [];
10011         
10012         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10013            c.remove();
10014         });
10015         
10016         this.syncValue();
10017     }
10018     
10019     
10020
10021     /** 
10022     * @cfg {Boolean} grow 
10023     * @hide 
10024     */
10025     /** 
10026     * @cfg {Number} growMin 
10027     * @hide 
10028     */
10029     /** 
10030     * @cfg {Number} growMax 
10031     * @hide 
10032     */
10033     /**
10034      * @hide
10035      * @method autoSize
10036      */
10037 });
10038 /*
10039  * Based on:
10040  * Ext JS Library 1.1.1
10041  * Copyright(c) 2006-2007, Ext JS, LLC.
10042  *
10043  * Originally Released Under LGPL - original licence link has changed is not relivant.
10044  *
10045  * Fork - LGPL
10046  * <script type="text/javascript">
10047  */
10048
10049 /**
10050  * @class Roo.View
10051  * @extends Roo.util.Observable
10052  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10053  * This class also supports single and multi selection modes. <br>
10054  * Create a data model bound view:
10055  <pre><code>
10056  var store = new Roo.data.Store(...);
10057
10058  var view = new Roo.View({
10059     el : "my-element",
10060     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10061  
10062     singleSelect: true,
10063     selectedClass: "ydataview-selected",
10064     store: store
10065  });
10066
10067  // listen for node click?
10068  view.on("click", function(vw, index, node, e){
10069  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10070  });
10071
10072  // load XML data
10073  dataModel.load("foobar.xml");
10074  </code></pre>
10075  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10076  * <br><br>
10077  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10078  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10079  * 
10080  * Note: old style constructor is still suported (container, template, config)
10081  * 
10082  * @constructor
10083  * Create a new View
10084  * @param {Object} config The config object
10085  * 
10086  */
10087 Roo.View = function(config, depreciated_tpl, depreciated_config){
10088     
10089     if (typeof(depreciated_tpl) == 'undefined') {
10090         // new way.. - universal constructor.
10091         Roo.apply(this, config);
10092         this.el  = Roo.get(this.el);
10093     } else {
10094         // old format..
10095         this.el  = Roo.get(config);
10096         this.tpl = depreciated_tpl;
10097         Roo.apply(this, depreciated_config);
10098     }
10099     this.wrapEl  = this.el.wrap().wrap();
10100     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10101     
10102     
10103     if(typeof(this.tpl) == "string"){
10104         this.tpl = new Roo.Template(this.tpl);
10105     } else {
10106         // support xtype ctors..
10107         this.tpl = new Roo.factory(this.tpl, Roo);
10108     }
10109     
10110     
10111     this.tpl.compile();
10112    
10113   
10114     
10115      
10116     /** @private */
10117     this.addEvents({
10118         /**
10119          * @event beforeclick
10120          * Fires before a click is processed. Returns false to cancel the default action.
10121          * @param {Roo.View} this
10122          * @param {Number} index The index of the target node
10123          * @param {HTMLElement} node The target node
10124          * @param {Roo.EventObject} e The raw event object
10125          */
10126             "beforeclick" : true,
10127         /**
10128          * @event click
10129          * Fires when a template node is clicked.
10130          * @param {Roo.View} this
10131          * @param {Number} index The index of the target node
10132          * @param {HTMLElement} node The target node
10133          * @param {Roo.EventObject} e The raw event object
10134          */
10135             "click" : true,
10136         /**
10137          * @event dblclick
10138          * Fires when a template node is double clicked.
10139          * @param {Roo.View} this
10140          * @param {Number} index The index of the target node
10141          * @param {HTMLElement} node The target node
10142          * @param {Roo.EventObject} e The raw event object
10143          */
10144             "dblclick" : true,
10145         /**
10146          * @event contextmenu
10147          * Fires when a template node is right clicked.
10148          * @param {Roo.View} this
10149          * @param {Number} index The index of the target node
10150          * @param {HTMLElement} node The target node
10151          * @param {Roo.EventObject} e The raw event object
10152          */
10153             "contextmenu" : true,
10154         /**
10155          * @event selectionchange
10156          * Fires when the selected nodes change.
10157          * @param {Roo.View} this
10158          * @param {Array} selections Array of the selected nodes
10159          */
10160             "selectionchange" : true,
10161     
10162         /**
10163          * @event beforeselect
10164          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10165          * @param {Roo.View} this
10166          * @param {HTMLElement} node The node to be selected
10167          * @param {Array} selections Array of currently selected nodes
10168          */
10169             "beforeselect" : true,
10170         /**
10171          * @event preparedata
10172          * Fires on every row to render, to allow you to change the data.
10173          * @param {Roo.View} this
10174          * @param {Object} data to be rendered (change this)
10175          */
10176           "preparedata" : true
10177           
10178           
10179         });
10180
10181
10182
10183     this.el.on({
10184         "click": this.onClick,
10185         "dblclick": this.onDblClick,
10186         "contextmenu": this.onContextMenu,
10187         scope:this
10188     });
10189
10190     this.selections = [];
10191     this.nodes = [];
10192     this.cmp = new Roo.CompositeElementLite([]);
10193     if(this.store){
10194         this.store = Roo.factory(this.store, Roo.data);
10195         this.setStore(this.store, true);
10196     }
10197     
10198     if ( this.footer && this.footer.xtype) {
10199            
10200          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10201         
10202         this.footer.dataSource = this.store
10203         this.footer.container = fctr;
10204         this.footer = Roo.factory(this.footer, Roo);
10205         fctr.insertFirst(this.el);
10206         
10207         // this is a bit insane - as the paging toolbar seems to detach the el..
10208 //        dom.parentNode.parentNode.parentNode
10209          // they get detached?
10210     }
10211     
10212     
10213     Roo.View.superclass.constructor.call(this);
10214     
10215     
10216 };
10217
10218 Roo.extend(Roo.View, Roo.util.Observable, {
10219     
10220      /**
10221      * @cfg {Roo.data.Store} store Data store to load data from.
10222      */
10223     store : false,
10224     
10225     /**
10226      * @cfg {String|Roo.Element} el The container element.
10227      */
10228     el : '',
10229     
10230     /**
10231      * @cfg {String|Roo.Template} tpl The template used by this View 
10232      */
10233     tpl : false,
10234     /**
10235      * @cfg {String} dataName the named area of the template to use as the data area
10236      *                          Works with domtemplates roo-name="name"
10237      */
10238     dataName: false,
10239     /**
10240      * @cfg {String} selectedClass The css class to add to selected nodes
10241      */
10242     selectedClass : "x-view-selected",
10243      /**
10244      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10245      */
10246     emptyText : "",
10247     
10248     /**
10249      * @cfg {String} text to display on mask (default Loading)
10250      */
10251     mask : false,
10252     /**
10253      * @cfg {Boolean} multiSelect Allow multiple selection
10254      */
10255     multiSelect : false,
10256     /**
10257      * @cfg {Boolean} singleSelect Allow single selection
10258      */
10259     singleSelect:  false,
10260     
10261     /**
10262      * @cfg {Boolean} toggleSelect - selecting 
10263      */
10264     toggleSelect : false,
10265     
10266     /**
10267      * Returns the element this view is bound to.
10268      * @return {Roo.Element}
10269      */
10270     getEl : function(){
10271         return this.wrapEl;
10272     },
10273     
10274     
10275
10276     /**
10277      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10278      */
10279     refresh : function(){
10280         Roo.log('refresh');
10281         var t = this.tpl;
10282         
10283         // if we are using something like 'domtemplate', then
10284         // the what gets used is:
10285         // t.applySubtemplate(NAME, data, wrapping data..)
10286         // the outer template then get' applied with
10287         //     the store 'extra data'
10288         // and the body get's added to the
10289         //      roo-name="data" node?
10290         //      <span class='roo-tpl-{name}'></span> ?????
10291         
10292         
10293         
10294         this.clearSelections();
10295         this.el.update("");
10296         var html = [];
10297         var records = this.store.getRange();
10298         if(records.length < 1) {
10299             
10300             // is this valid??  = should it render a template??
10301             
10302             this.el.update(this.emptyText);
10303             return;
10304         }
10305         var el = this.el;
10306         if (this.dataName) {
10307             this.el.update(t.apply(this.store.meta)); //????
10308             el = this.el.child('.roo-tpl-' + this.dataName);
10309         }
10310         
10311         for(var i = 0, len = records.length; i < len; i++){
10312             var data = this.prepareData(records[i].data, i, records[i]);
10313             this.fireEvent("preparedata", this, data, i, records[i]);
10314             html[html.length] = Roo.util.Format.trim(
10315                 this.dataName ?
10316                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10317                     t.apply(data)
10318             );
10319         }
10320         
10321         
10322         
10323         el.update(html.join(""));
10324         this.nodes = el.dom.childNodes;
10325         this.updateIndexes(0);
10326     },
10327     
10328
10329     /**
10330      * Function to override to reformat the data that is sent to
10331      * the template for each node.
10332      * DEPRICATED - use the preparedata event handler.
10333      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10334      * a JSON object for an UpdateManager bound view).
10335      */
10336     prepareData : function(data, index, record)
10337     {
10338         this.fireEvent("preparedata", this, data, index, record);
10339         return data;
10340     },
10341
10342     onUpdate : function(ds, record){
10343          Roo.log('on update');   
10344         this.clearSelections();
10345         var index = this.store.indexOf(record);
10346         var n = this.nodes[index];
10347         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10348         n.parentNode.removeChild(n);
10349         this.updateIndexes(index, index);
10350     },
10351
10352     
10353     
10354 // --------- FIXME     
10355     onAdd : function(ds, records, index)
10356     {
10357         Roo.log(['on Add', ds, records, index] );        
10358         this.clearSelections();
10359         if(this.nodes.length == 0){
10360             this.refresh();
10361             return;
10362         }
10363         var n = this.nodes[index];
10364         for(var i = 0, len = records.length; i < len; i++){
10365             var d = this.prepareData(records[i].data, i, records[i]);
10366             if(n){
10367                 this.tpl.insertBefore(n, d);
10368             }else{
10369                 
10370                 this.tpl.append(this.el, d);
10371             }
10372         }
10373         this.updateIndexes(index);
10374     },
10375
10376     onRemove : function(ds, record, index){
10377         Roo.log('onRemove');
10378         this.clearSelections();
10379         var el = this.dataName  ?
10380             this.el.child('.roo-tpl-' + this.dataName) :
10381             this.el; 
10382         
10383         el.dom.removeChild(this.nodes[index]);
10384         this.updateIndexes(index);
10385     },
10386
10387     /**
10388      * Refresh an individual node.
10389      * @param {Number} index
10390      */
10391     refreshNode : function(index){
10392         this.onUpdate(this.store, this.store.getAt(index));
10393     },
10394
10395     updateIndexes : function(startIndex, endIndex){
10396         var ns = this.nodes;
10397         startIndex = startIndex || 0;
10398         endIndex = endIndex || ns.length - 1;
10399         for(var i = startIndex; i <= endIndex; i++){
10400             ns[i].nodeIndex = i;
10401         }
10402     },
10403
10404     /**
10405      * Changes the data store this view uses and refresh the view.
10406      * @param {Store} store
10407      */
10408     setStore : function(store, initial){
10409         if(!initial && this.store){
10410             this.store.un("datachanged", this.refresh);
10411             this.store.un("add", this.onAdd);
10412             this.store.un("remove", this.onRemove);
10413             this.store.un("update", this.onUpdate);
10414             this.store.un("clear", this.refresh);
10415             this.store.un("beforeload", this.onBeforeLoad);
10416             this.store.un("load", this.onLoad);
10417             this.store.un("loadexception", this.onLoad);
10418         }
10419         if(store){
10420           
10421             store.on("datachanged", this.refresh, this);
10422             store.on("add", this.onAdd, this);
10423             store.on("remove", this.onRemove, this);
10424             store.on("update", this.onUpdate, this);
10425             store.on("clear", this.refresh, this);
10426             store.on("beforeload", this.onBeforeLoad, this);
10427             store.on("load", this.onLoad, this);
10428             store.on("loadexception", this.onLoad, this);
10429         }
10430         
10431         if(store){
10432             this.refresh();
10433         }
10434     },
10435     /**
10436      * onbeforeLoad - masks the loading area.
10437      *
10438      */
10439     onBeforeLoad : function(store,opts)
10440     {
10441          Roo.log('onBeforeLoad');   
10442         if (!opts.add) {
10443             this.el.update("");
10444         }
10445         this.el.mask(this.mask ? this.mask : "Loading" ); 
10446     },
10447     onLoad : function ()
10448     {
10449         this.el.unmask();
10450     },
10451     
10452
10453     /**
10454      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10455      * @param {HTMLElement} node
10456      * @return {HTMLElement} The template node
10457      */
10458     findItemFromChild : function(node){
10459         var el = this.dataName  ?
10460             this.el.child('.roo-tpl-' + this.dataName,true) :
10461             this.el.dom; 
10462         
10463         if(!node || node.parentNode == el){
10464                     return node;
10465             }
10466             var p = node.parentNode;
10467             while(p && p != el){
10468             if(p.parentNode == el){
10469                 return p;
10470             }
10471             p = p.parentNode;
10472         }
10473             return null;
10474     },
10475
10476     /** @ignore */
10477     onClick : function(e){
10478         var item = this.findItemFromChild(e.getTarget());
10479         if(item){
10480             var index = this.indexOf(item);
10481             if(this.onItemClick(item, index, e) !== false){
10482                 this.fireEvent("click", this, index, item, e);
10483             }
10484         }else{
10485             this.clearSelections();
10486         }
10487     },
10488
10489     /** @ignore */
10490     onContextMenu : function(e){
10491         var item = this.findItemFromChild(e.getTarget());
10492         if(item){
10493             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10494         }
10495     },
10496
10497     /** @ignore */
10498     onDblClick : function(e){
10499         var item = this.findItemFromChild(e.getTarget());
10500         if(item){
10501             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10502         }
10503     },
10504
10505     onItemClick : function(item, index, e)
10506     {
10507         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10508             return false;
10509         }
10510         if (this.toggleSelect) {
10511             var m = this.isSelected(item) ? 'unselect' : 'select';
10512             Roo.log(m);
10513             var _t = this;
10514             _t[m](item, true, false);
10515             return true;
10516         }
10517         if(this.multiSelect || this.singleSelect){
10518             if(this.multiSelect && e.shiftKey && this.lastSelection){
10519                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10520             }else{
10521                 this.select(item, this.multiSelect && e.ctrlKey);
10522                 this.lastSelection = item;
10523             }
10524             e.preventDefault();
10525         }
10526         return true;
10527     },
10528
10529     /**
10530      * Get the number of selected nodes.
10531      * @return {Number}
10532      */
10533     getSelectionCount : function(){
10534         return this.selections.length;
10535     },
10536
10537     /**
10538      * Get the currently selected nodes.
10539      * @return {Array} An array of HTMLElements
10540      */
10541     getSelectedNodes : function(){
10542         return this.selections;
10543     },
10544
10545     /**
10546      * Get the indexes of the selected nodes.
10547      * @return {Array}
10548      */
10549     getSelectedIndexes : function(){
10550         var indexes = [], s = this.selections;
10551         for(var i = 0, len = s.length; i < len; i++){
10552             indexes.push(s[i].nodeIndex);
10553         }
10554         return indexes;
10555     },
10556
10557     /**
10558      * Clear all selections
10559      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10560      */
10561     clearSelections : function(suppressEvent){
10562         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10563             this.cmp.elements = this.selections;
10564             this.cmp.removeClass(this.selectedClass);
10565             this.selections = [];
10566             if(!suppressEvent){
10567                 this.fireEvent("selectionchange", this, this.selections);
10568             }
10569         }
10570     },
10571
10572     /**
10573      * Returns true if the passed node is selected
10574      * @param {HTMLElement/Number} node The node or node index
10575      * @return {Boolean}
10576      */
10577     isSelected : function(node){
10578         var s = this.selections;
10579         if(s.length < 1){
10580             return false;
10581         }
10582         node = this.getNode(node);
10583         return s.indexOf(node) !== -1;
10584     },
10585
10586     /**
10587      * Selects nodes.
10588      * @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
10589      * @param {Boolean} keepExisting (optional) true to keep existing selections
10590      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10591      */
10592     select : function(nodeInfo, keepExisting, suppressEvent){
10593         if(nodeInfo instanceof Array){
10594             if(!keepExisting){
10595                 this.clearSelections(true);
10596             }
10597             for(var i = 0, len = nodeInfo.length; i < len; i++){
10598                 this.select(nodeInfo[i], true, true);
10599             }
10600             return;
10601         } 
10602         var node = this.getNode(nodeInfo);
10603         if(!node || this.isSelected(node)){
10604             return; // already selected.
10605         }
10606         if(!keepExisting){
10607             this.clearSelections(true);
10608         }
10609         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10610             Roo.fly(node).addClass(this.selectedClass);
10611             this.selections.push(node);
10612             if(!suppressEvent){
10613                 this.fireEvent("selectionchange", this, this.selections);
10614             }
10615         }
10616         
10617         
10618     },
10619       /**
10620      * Unselects nodes.
10621      * @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
10622      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10623      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10624      */
10625     unselect : function(nodeInfo, keepExisting, suppressEvent)
10626     {
10627         if(nodeInfo instanceof Array){
10628             Roo.each(this.selections, function(s) {
10629                 this.unselect(s, nodeInfo);
10630             }, this);
10631             return;
10632         }
10633         var node = this.getNode(nodeInfo);
10634         if(!node || !this.isSelected(node)){
10635             Roo.log("not selected");
10636             return; // not selected.
10637         }
10638         // fireevent???
10639         var ns = [];
10640         Roo.each(this.selections, function(s) {
10641             if (s == node ) {
10642                 Roo.fly(node).removeClass(this.selectedClass);
10643
10644                 return;
10645             }
10646             ns.push(s);
10647         },this);
10648         
10649         this.selections= ns;
10650         this.fireEvent("selectionchange", this, this.selections);
10651     },
10652
10653     /**
10654      * Gets a template node.
10655      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10656      * @return {HTMLElement} The node or null if it wasn't found
10657      */
10658     getNode : function(nodeInfo){
10659         if(typeof nodeInfo == "string"){
10660             return document.getElementById(nodeInfo);
10661         }else if(typeof nodeInfo == "number"){
10662             return this.nodes[nodeInfo];
10663         }
10664         return nodeInfo;
10665     },
10666
10667     /**
10668      * Gets a range template nodes.
10669      * @param {Number} startIndex
10670      * @param {Number} endIndex
10671      * @return {Array} An array of nodes
10672      */
10673     getNodes : function(start, end){
10674         var ns = this.nodes;
10675         start = start || 0;
10676         end = typeof end == "undefined" ? ns.length - 1 : end;
10677         var nodes = [];
10678         if(start <= end){
10679             for(var i = start; i <= end; i++){
10680                 nodes.push(ns[i]);
10681             }
10682         } else{
10683             for(var i = start; i >= end; i--){
10684                 nodes.push(ns[i]);
10685             }
10686         }
10687         return nodes;
10688     },
10689
10690     /**
10691      * Finds the index of the passed node
10692      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10693      * @return {Number} The index of the node or -1
10694      */
10695     indexOf : function(node){
10696         node = this.getNode(node);
10697         if(typeof node.nodeIndex == "number"){
10698             return node.nodeIndex;
10699         }
10700         var ns = this.nodes;
10701         for(var i = 0, len = ns.length; i < len; i++){
10702             if(ns[i] == node){
10703                 return i;
10704             }
10705         }
10706         return -1;
10707     }
10708 });
10709 /*
10710  * - LGPL
10711  *
10712  * based on jquery fullcalendar
10713  * 
10714  */
10715
10716 Roo.bootstrap = Roo.bootstrap || {};
10717 /**
10718  * @class Roo.bootstrap.Calendar
10719  * @extends Roo.bootstrap.Component
10720  * Bootstrap Calendar class
10721  * @cfg {Boolean} loadMask (true|false) default false
10722  * @cfg {Object} header generate the user specific header of the calendar, default false
10723
10724  * @constructor
10725  * Create a new Container
10726  * @param {Object} config The config object
10727  */
10728
10729
10730
10731 Roo.bootstrap.Calendar = function(config){
10732     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10733      this.addEvents({
10734         /**
10735              * @event select
10736              * Fires when a date is selected
10737              * @param {DatePicker} this
10738              * @param {Date} date The selected date
10739              */
10740         'select': true,
10741         /**
10742              * @event monthchange
10743              * Fires when the displayed month changes 
10744              * @param {DatePicker} this
10745              * @param {Date} date The selected month
10746              */
10747         'monthchange': true,
10748         /**
10749              * @event evententer
10750              * Fires when mouse over an event
10751              * @param {Calendar} this
10752              * @param {event} Event
10753              */
10754         'evententer': true,
10755         /**
10756              * @event eventleave
10757              * Fires when the mouse leaves an
10758              * @param {Calendar} this
10759              * @param {event}
10760              */
10761         'eventleave': true,
10762         /**
10763              * @event eventclick
10764              * Fires when the mouse click an
10765              * @param {Calendar} this
10766              * @param {event}
10767              */
10768         'eventclick': true
10769         
10770     });
10771
10772 };
10773
10774 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10775     
10776      /**
10777      * @cfg {Number} startDay
10778      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10779      */
10780     startDay : 0,
10781     
10782     loadMask : false,
10783     
10784     header : false,
10785       
10786     getAutoCreate : function(){
10787         
10788         
10789         var fc_button = function(name, corner, style, content ) {
10790             return Roo.apply({},{
10791                 tag : 'span',
10792                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10793                          (corner.length ?
10794                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10795                             ''
10796                         ),
10797                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10798                 unselectable: 'on'
10799             });
10800         };
10801         
10802         var header = {};
10803         
10804         if(!this.header){
10805             header = {
10806                 tag : 'table',
10807                 cls : 'fc-header',
10808                 style : 'width:100%',
10809                 cn : [
10810                     {
10811                         tag: 'tr',
10812                         cn : [
10813                             {
10814                                 tag : 'td',
10815                                 cls : 'fc-header-left',
10816                                 cn : [
10817                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
10818                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
10819                                     { tag: 'span', cls: 'fc-header-space' },
10820                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
10821
10822
10823                                 ]
10824                             },
10825
10826                             {
10827                                 tag : 'td',
10828                                 cls : 'fc-header-center',
10829                                 cn : [
10830                                     {
10831                                         tag: 'span',
10832                                         cls: 'fc-header-title',
10833                                         cn : {
10834                                             tag: 'H2',
10835                                             html : 'month / year'
10836                                         }
10837                                     }
10838
10839                                 ]
10840                             },
10841                             {
10842                                 tag : 'td',
10843                                 cls : 'fc-header-right',
10844                                 cn : [
10845                               /*      fc_button('month', 'left', '', 'month' ),
10846                                     fc_button('week', '', '', 'week' ),
10847                                     fc_button('day', 'right', '', 'day' )
10848                                 */    
10849
10850                                 ]
10851                             }
10852
10853                         ]
10854                     }
10855                 ]
10856             };
10857         }
10858         
10859         header = this.header;
10860         
10861        
10862         var cal_heads = function() {
10863             var ret = [];
10864             // fixme - handle this.
10865             
10866             for (var i =0; i < Date.dayNames.length; i++) {
10867                 var d = Date.dayNames[i];
10868                 ret.push({
10869                     tag: 'th',
10870                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10871                     html : d.substring(0,3)
10872                 });
10873                 
10874             }
10875             ret[0].cls += ' fc-first';
10876             ret[6].cls += ' fc-last';
10877             return ret;
10878         };
10879         var cal_cell = function(n) {
10880             return  {
10881                 tag: 'td',
10882                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10883                 cn : [
10884                     {
10885                         cn : [
10886                             {
10887                                 cls: 'fc-day-number',
10888                                 html: 'D'
10889                             },
10890                             {
10891                                 cls: 'fc-day-content',
10892                              
10893                                 cn : [
10894                                      {
10895                                         style: 'position: relative;' // height: 17px;
10896                                     }
10897                                 ]
10898                             }
10899                             
10900                             
10901                         ]
10902                     }
10903                 ]
10904                 
10905             }
10906         };
10907         var cal_rows = function() {
10908             
10909             var ret = []
10910             for (var r = 0; r < 6; r++) {
10911                 var row= {
10912                     tag : 'tr',
10913                     cls : 'fc-week',
10914                     cn : []
10915                 };
10916                 
10917                 for (var i =0; i < Date.dayNames.length; i++) {
10918                     var d = Date.dayNames[i];
10919                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10920
10921                 }
10922                 row.cn[0].cls+=' fc-first';
10923                 row.cn[0].cn[0].style = 'min-height:90px';
10924                 row.cn[6].cls+=' fc-last';
10925                 ret.push(row);
10926                 
10927             }
10928             ret[0].cls += ' fc-first';
10929             ret[4].cls += ' fc-prev-last';
10930             ret[5].cls += ' fc-last';
10931             return ret;
10932             
10933         };
10934         
10935         var cal_table = {
10936             tag: 'table',
10937             cls: 'fc-border-separate',
10938             style : 'width:100%',
10939             cellspacing  : 0,
10940             cn : [
10941                 { 
10942                     tag: 'thead',
10943                     cn : [
10944                         { 
10945                             tag: 'tr',
10946                             cls : 'fc-first fc-last',
10947                             cn : cal_heads()
10948                         }
10949                     ]
10950                 },
10951                 { 
10952                     tag: 'tbody',
10953                     cn : cal_rows()
10954                 }
10955                   
10956             ]
10957         };
10958          
10959          var cfg = {
10960             cls : 'fc fc-ltr',
10961             cn : [
10962                 header,
10963                 {
10964                     cls : 'fc-content',
10965                     style : "position: relative;",
10966                     cn : [
10967                         {
10968                             cls : 'fc-view fc-view-month fc-grid',
10969                             style : 'position: relative',
10970                             unselectable : 'on',
10971                             cn : [
10972                                 {
10973                                     cls : 'fc-event-container',
10974                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10975                                 },
10976                                 cal_table
10977                             ]
10978                         }
10979                     ]
10980     
10981                 }
10982            ] 
10983             
10984         };
10985         
10986          
10987         
10988         return cfg;
10989     },
10990     
10991     
10992     initEvents : function()
10993     {
10994         if(!this.store){
10995             throw "can not find store for calendar";
10996         }
10997         
10998         var mark = {
10999             tag: "div",
11000             cls:"x-dlg-mask",
11001             style: "text-align:center",
11002             cn: [
11003                 {
11004                     tag: "div",
11005                     style: "background-color:white;width:50%;margin:250 auto",
11006                     cn: [
11007                         {
11008                             tag: "img",
11009                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11010                         },
11011                         {
11012                             tag: "span",
11013                             html: "Loading"
11014                         }
11015                         
11016                     ]
11017                 }
11018             ]
11019         }
11020         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11021         
11022         var size = this.el.select('.fc-content', true).first().getSize();
11023         this.maskEl.setSize(size.width, size.height);
11024         this.maskEl.enableDisplayMode("block");
11025         if(!this.loadMask){
11026             this.maskEl.hide();
11027         }
11028         
11029         this.store = Roo.factory(this.store, Roo.data);
11030         this.store.on('load', this.onLoad, this);
11031         this.store.on('beforeload', this.onBeforeLoad, this);
11032         
11033         this.resize();
11034         
11035         this.cells = this.el.select('.fc-day',true);
11036         //Roo.log(this.cells);
11037         this.textNodes = this.el.query('.fc-day-number');
11038         this.cells.addClassOnOver('fc-state-hover');
11039         
11040         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11041         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11042         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11043         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11044         
11045         this.on('monthchange', this.onMonthChange, this);
11046         
11047         this.update(new Date().clearTime());
11048     },
11049     
11050     resize : function() {
11051         var sz  = this.el.getSize();
11052         
11053         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11054         this.el.select('.fc-day-content div',true).setHeight(34);
11055     },
11056     
11057     
11058     // private
11059     showPrevMonth : function(e){
11060         this.update(this.activeDate.add("mo", -1));
11061     },
11062     showToday : function(e){
11063         this.update(new Date().clearTime());
11064     },
11065     // private
11066     showNextMonth : function(e){
11067         this.update(this.activeDate.add("mo", 1));
11068     },
11069
11070     // private
11071     showPrevYear : function(){
11072         this.update(this.activeDate.add("y", -1));
11073     },
11074
11075     // private
11076     showNextYear : function(){
11077         this.update(this.activeDate.add("y", 1));
11078     },
11079
11080     
11081    // private
11082     update : function(date)
11083     {
11084         var vd = this.activeDate;
11085         this.activeDate = date;
11086 //        if(vd && this.el){
11087 //            var t = date.getTime();
11088 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11089 //                Roo.log('using add remove');
11090 //                
11091 //                this.fireEvent('monthchange', this, date);
11092 //                
11093 //                this.cells.removeClass("fc-state-highlight");
11094 //                this.cells.each(function(c){
11095 //                   if(c.dateValue == t){
11096 //                       c.addClass("fc-state-highlight");
11097 //                       setTimeout(function(){
11098 //                            try{c.dom.firstChild.focus();}catch(e){}
11099 //                       }, 50);
11100 //                       return false;
11101 //                   }
11102 //                   return true;
11103 //                });
11104 //                return;
11105 //            }
11106 //        }
11107         
11108         var days = date.getDaysInMonth();
11109         
11110         var firstOfMonth = date.getFirstDateOfMonth();
11111         var startingPos = firstOfMonth.getDay()-this.startDay;
11112         
11113         if(startingPos < this.startDay){
11114             startingPos += 7;
11115         }
11116         
11117         var pm = date.add(Date.MONTH, -1);
11118         var prevStart = pm.getDaysInMonth()-startingPos;
11119 //        
11120         this.cells = this.el.select('.fc-day',true);
11121         this.textNodes = this.el.query('.fc-day-number');
11122         this.cells.addClassOnOver('fc-state-hover');
11123         
11124         var cells = this.cells.elements;
11125         var textEls = this.textNodes;
11126         
11127         Roo.each(cells, function(cell){
11128             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11129         });
11130         
11131         days += startingPos;
11132
11133         // convert everything to numbers so it's fast
11134         var day = 86400000;
11135         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11136         //Roo.log(d);
11137         //Roo.log(pm);
11138         //Roo.log(prevStart);
11139         
11140         var today = new Date().clearTime().getTime();
11141         var sel = date.clearTime().getTime();
11142         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11143         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11144         var ddMatch = this.disabledDatesRE;
11145         var ddText = this.disabledDatesText;
11146         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11147         var ddaysText = this.disabledDaysText;
11148         var format = this.format;
11149         
11150         var setCellClass = function(cal, cell){
11151             
11152             //Roo.log('set Cell Class');
11153             cell.title = "";
11154             var t = d.getTime();
11155             
11156             //Roo.log(d);
11157             
11158             cell.dateValue = t;
11159             if(t == today){
11160                 cell.className += " fc-today";
11161                 cell.className += " fc-state-highlight";
11162                 cell.title = cal.todayText;
11163             }
11164             if(t == sel){
11165                 // disable highlight in other month..
11166                 //cell.className += " fc-state-highlight";
11167                 
11168             }
11169             // disabling
11170             if(t < min) {
11171                 cell.className = " fc-state-disabled";
11172                 cell.title = cal.minText;
11173                 return;
11174             }
11175             if(t > max) {
11176                 cell.className = " fc-state-disabled";
11177                 cell.title = cal.maxText;
11178                 return;
11179             }
11180             if(ddays){
11181                 if(ddays.indexOf(d.getDay()) != -1){
11182                     cell.title = ddaysText;
11183                     cell.className = " fc-state-disabled";
11184                 }
11185             }
11186             if(ddMatch && format){
11187                 var fvalue = d.dateFormat(format);
11188                 if(ddMatch.test(fvalue)){
11189                     cell.title = ddText.replace("%0", fvalue);
11190                     cell.className = " fc-state-disabled";
11191                 }
11192             }
11193             
11194             if (!cell.initialClassName) {
11195                 cell.initialClassName = cell.dom.className;
11196             }
11197             
11198             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11199         };
11200
11201         var i = 0;
11202         
11203         for(; i < startingPos; i++) {
11204             textEls[i].innerHTML = (++prevStart);
11205             d.setDate(d.getDate()+1);
11206             
11207             cells[i].className = "fc-past fc-other-month";
11208             setCellClass(this, cells[i]);
11209         }
11210         
11211         var intDay = 0;
11212         
11213         for(; i < days; i++){
11214             intDay = i - startingPos + 1;
11215             textEls[i].innerHTML = (intDay);
11216             d.setDate(d.getDate()+1);
11217             
11218             cells[i].className = ''; // "x-date-active";
11219             setCellClass(this, cells[i]);
11220         }
11221         var extraDays = 0;
11222         
11223         for(; i < 42; i++) {
11224             textEls[i].innerHTML = (++extraDays);
11225             d.setDate(d.getDate()+1);
11226             
11227             cells[i].className = "fc-future fc-other-month";
11228             setCellClass(this, cells[i]);
11229         }
11230         
11231         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11232         
11233         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11234         
11235         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11236         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11237         
11238         if(totalRows != 6){
11239             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11240             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11241         }
11242         
11243         this.fireEvent('monthchange', this, date);
11244         
11245         
11246         /*
11247         if(!this.internalRender){
11248             var main = this.el.dom.firstChild;
11249             var w = main.offsetWidth;
11250             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11251             Roo.fly(main).setWidth(w);
11252             this.internalRender = true;
11253             // opera does not respect the auto grow header center column
11254             // then, after it gets a width opera refuses to recalculate
11255             // without a second pass
11256             if(Roo.isOpera && !this.secondPass){
11257                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11258                 this.secondPass = true;
11259                 this.update.defer(10, this, [date]);
11260             }
11261         }
11262         */
11263         
11264     },
11265     
11266     findCell : function(dt) {
11267         dt = dt.clearTime().getTime();
11268         var ret = false;
11269         this.cells.each(function(c){
11270             //Roo.log("check " +c.dateValue + '?=' + dt);
11271             if(c.dateValue == dt){
11272                 ret = c;
11273                 return false;
11274             }
11275             return true;
11276         });
11277         
11278         return ret;
11279     },
11280     
11281     findCells : function(ev) {
11282         var s = ev.start.clone().clearTime().getTime();
11283        // Roo.log(s);
11284         var e= ev.end.clone().clearTime().getTime();
11285        // Roo.log(e);
11286         var ret = [];
11287         this.cells.each(function(c){
11288              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11289             
11290             if(c.dateValue > e){
11291                 return ;
11292             }
11293             if(c.dateValue < s){
11294                 return ;
11295             }
11296             ret.push(c);
11297         });
11298         
11299         return ret;    
11300     },
11301     
11302     findBestRow: function(cells)
11303     {
11304         var ret = 0;
11305         
11306         for (var i =0 ; i < cells.length;i++) {
11307             ret  = Math.max(cells[i].rows || 0,ret);
11308         }
11309         return ret;
11310         
11311     },
11312     
11313     
11314     addItem : function(ev)
11315     {
11316         // look for vertical location slot in
11317         var cells = this.findCells(ev);
11318         
11319         ev.row = this.findBestRow(cells);
11320         
11321         // work out the location.
11322         
11323         var crow = false;
11324         var rows = [];
11325         for(var i =0; i < cells.length; i++) {
11326             if (!crow) {
11327                 crow = {
11328                     start : cells[i],
11329                     end :  cells[i]
11330                 };
11331                 continue;
11332             }
11333             if (crow.start.getY() == cells[i].getY()) {
11334                 // on same row.
11335                 crow.end = cells[i];
11336                 continue;
11337             }
11338             // different row.
11339             rows.push(crow);
11340             crow = {
11341                 start: cells[i],
11342                 end : cells[i]
11343             };
11344             
11345         }
11346         
11347         rows.push(crow);
11348         ev.els = [];
11349         ev.rows = rows;
11350         ev.cells = cells;
11351         for (var i = 0; i < cells.length;i++) {
11352             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11353             
11354         }
11355         
11356         this.calevents.push(ev);
11357     },
11358     
11359     clearEvents: function() {
11360         
11361         if(!this.calevents){
11362             return;
11363         }
11364         
11365         Roo.each(this.cells.elements, function(c){
11366             c.rows = 0;
11367         });
11368         
11369         Roo.each(this.calevents, function(e) {
11370             Roo.each(e.els, function(el) {
11371                 el.un('mouseenter' ,this.onEventEnter, this);
11372                 el.un('mouseleave' ,this.onEventLeave, this);
11373                 el.remove();
11374             },this);
11375         },this);
11376         
11377     },
11378     
11379     renderEvents: function()
11380     {   
11381         // first make sure there is enough space..
11382         
11383         this.cells.each(function(c) {
11384         
11385             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11386         });
11387         
11388         for (var e = 0; e < this.calevents.length; e++) {
11389             var ev = this.calevents[e];
11390             var cells = ev.cells;
11391             var rows = ev.rows;
11392             
11393             for(var i =0; i < rows.length; i++) {
11394                 
11395                  
11396                 // how many rows should it span..
11397                 
11398                 var  cfg = {
11399                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11400                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11401                     
11402                     unselectable : "on",
11403                     cn : [
11404                         {
11405                             cls: 'fc-event-inner',
11406                             cn : [
11407 //                                {
11408 //                                  tag:'span',
11409 //                                  cls: 'fc-event-time',
11410 //                                  html : cells.length > 1 ? '' : ev.time
11411 //                                },
11412                                 {
11413                                   tag:'span',
11414                                   cls: 'fc-event-title',
11415                                   html : String.format('{0}', ev.title)
11416                                 }
11417                                 
11418                                 
11419                             ]
11420                         },
11421                         {
11422                             cls: 'ui-resizable-handle ui-resizable-e',
11423                             html : '&nbsp;&nbsp;&nbsp'
11424                         }
11425                         
11426                     ]
11427                 };
11428                 if (i == 0) {
11429                     cfg.cls += ' fc-event-start';
11430                 }
11431                 if ((i+1) == rows.length) {
11432                     cfg.cls += ' fc-event-end';
11433                 }
11434                 
11435                 var ctr = this.el.select('.fc-event-container',true).first();
11436                 var cg = ctr.createChild(cfg);
11437                 
11438                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11439                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11440                 cg.on('click', this.onEventClick, this, ev);
11441                 
11442                 ev.els.push(cg);
11443                 
11444                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11445                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11446                 //Roo.log(cg);
11447                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11448                 cg.setWidth(ebox.right - sbox.x -2);
11449             }
11450             
11451             
11452         }
11453         
11454     },
11455     
11456     onEventEnter: function (e, el,event,d) {
11457         this.fireEvent('evententer', this, el, event);
11458     },
11459     
11460     onEventLeave: function (e, el,event,d) {
11461         this.fireEvent('eventleave', this, el, event);
11462     },
11463     
11464     onEventClick: function (e, el,event,d) {
11465         this.fireEvent('eventclick', this, el, event);
11466     },
11467     
11468     onMonthChange: function () {
11469         this.store.load();
11470     },
11471     
11472     onLoad: function () 
11473     {   
11474         this.calevents = [];
11475         var cal = this;
11476         
11477         if(this.store.getCount() > 0){
11478             this.store.data.each(function(d){
11479                cal.addItem({
11480                     id : d.data.id,
11481                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11482                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11483                     time : d.data.start_time,
11484                     title : d.data.title,
11485                     description : d.data.description,
11486                     venue : d.data.venue
11487                 });
11488             });
11489         }
11490         
11491         this.renderEvents();
11492         
11493         if(this.loadMask){
11494             this.maskEl.hide();
11495         }
11496     },
11497     
11498     onBeforeLoad: function()
11499     {
11500         this.clearEvents();
11501         
11502         if(this.loadMask){
11503             this.maskEl.show();
11504         }
11505     }
11506 });
11507
11508  
11509  /*
11510  * - LGPL
11511  *
11512  * element
11513  * 
11514  */
11515
11516 /**
11517  * @class Roo.bootstrap.Popover
11518  * @extends Roo.bootstrap.Component
11519  * Bootstrap Popover class
11520  * @cfg {String} html contents of the popover   (or false to use children..)
11521  * @cfg {String} title of popover (or false to hide)
11522  * @cfg {String} placement how it is placed
11523  * @cfg {String} trigger click || hover (or false to trigger manually)
11524  * @cfg {String} over what (parent or false to trigger manually.)
11525  * 
11526  * @constructor
11527  * Create a new Popover
11528  * @param {Object} config The config object
11529  */
11530
11531 Roo.bootstrap.Popover = function(config){
11532     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11533 };
11534
11535 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11536     
11537     title: 'Fill in a title',
11538     html: false,
11539     
11540     placement : 'right',
11541     trigger : 'hover', // hover
11542     
11543     over: 'parent',
11544     
11545     can_build_overlaid : false,
11546     
11547     getChildContainer : function()
11548     {
11549         return this.el.select('.popover-content',true).first();
11550     },
11551     
11552     getAutoCreate : function(){
11553          Roo.log('make popover?');
11554         var cfg = {
11555            cls : 'popover roo-dynamic',
11556            style: 'display:block',
11557            cn : [
11558                 {
11559                     cls : 'arrow'
11560                 },
11561                 {
11562                     cls : 'popover-inner',
11563                     cn : [
11564                         {
11565                             tag: 'h3',
11566                             cls: 'popover-title',
11567                             html : this.title
11568                         },
11569                         {
11570                             cls : 'popover-content',
11571                             html : this.html
11572                         }
11573                     ]
11574                     
11575                 }
11576            ]
11577         };
11578         
11579         return cfg;
11580     },
11581     setTitle: function(str)
11582     {
11583         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11584     },
11585     setContent: function(str)
11586     {
11587         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11588     },
11589     // as it get's added to the bottom of the page.
11590     onRender : function(ct, position)
11591     {
11592         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11593         if(!this.el){
11594             var cfg = Roo.apply({},  this.getAutoCreate());
11595             cfg.id = Roo.id();
11596             
11597             if (this.cls) {
11598                 cfg.cls += ' ' + this.cls;
11599             }
11600             if (this.style) {
11601                 cfg.style = this.style;
11602             }
11603             Roo.log("adding to ")
11604             this.el = Roo.get(document.body).createChild(cfg, position);
11605             Roo.log(this.el);
11606         }
11607         this.initEvents();
11608     },
11609     
11610     initEvents : function()
11611     {
11612         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11613         this.el.enableDisplayMode('block');
11614         this.el.hide();
11615         if (this.over === false) {
11616             return; 
11617         }
11618         if (this.triggers === false) {
11619             return;
11620         }
11621         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11622         var triggers = this.trigger ? this.trigger.split(' ') : [];
11623         Roo.each(triggers, function(trigger) {
11624         
11625             if (trigger == 'click') {
11626                 on_el.on('click', this.toggle, this);
11627             } else if (trigger != 'manual') {
11628                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11629                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11630       
11631                 on_el.on(eventIn  ,this.enter, this);
11632                 on_el.on(eventOut, this.leave, this);
11633             }
11634         }, this);
11635         
11636     },
11637     
11638     
11639     // private
11640     timeout : null,
11641     hoverState : null,
11642     
11643     toggle : function () {
11644         this.hoverState == 'in' ? this.leave() : this.enter();
11645     },
11646     
11647     enter : function () {
11648        
11649     
11650         clearTimeout(this.timeout);
11651     
11652         this.hoverState = 'in'
11653     
11654         if (!this.delay || !this.delay.show) {
11655             this.show();
11656             return 
11657         }
11658         var _t = this;
11659         this.timeout = setTimeout(function () {
11660             if (_t.hoverState == 'in') {
11661                 _t.show();
11662             }
11663         }, this.delay.show)
11664     },
11665     leave : function() {
11666         clearTimeout(this.timeout);
11667     
11668         this.hoverState = 'out'
11669     
11670         if (!this.delay || !this.delay.hide) {
11671             this.hide();
11672             return 
11673         }
11674         var _t = this;
11675         this.timeout = setTimeout(function () {
11676             if (_t.hoverState == 'out') {
11677                 _t.hide();
11678             }
11679         }, this.delay.hide)
11680     },
11681     
11682     show : function (on_el)
11683     {
11684         if (!on_el) {
11685             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11686         }
11687         // set content.
11688         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11689         if (this.html !== false) {
11690             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11691         }
11692         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11693         if (!this.title.length) {
11694             this.el.select('.popover-title',true).hide();
11695         }
11696         
11697         var placement = typeof this.placement == 'function' ?
11698             this.placement.call(this, this.el, on_el) :
11699             this.placement;
11700             
11701         var autoToken = /\s?auto?\s?/i;
11702         var autoPlace = autoToken.test(placement);
11703         if (autoPlace) {
11704             placement = placement.replace(autoToken, '') || 'top';
11705         }
11706         
11707         //this.el.detach()
11708         //this.el.setXY([0,0]);
11709         this.el.show();
11710         this.el.dom.style.display='block';
11711         this.el.addClass(placement);
11712         
11713         //this.el.appendTo(on_el);
11714         
11715         var p = this.getPosition();
11716         var box = this.el.getBox();
11717         
11718         if (autoPlace) {
11719             // fixme..
11720         }
11721         var align = Roo.bootstrap.Popover.alignment[placement]
11722         this.el.alignTo(on_el, align[0],align[1]);
11723         //var arrow = this.el.select('.arrow',true).first();
11724         //arrow.set(align[2], 
11725         
11726         this.el.addClass('in');
11727         this.hoverState = null;
11728         
11729         if (this.el.hasClass('fade')) {
11730             // fade it?
11731         }
11732         
11733     },
11734     hide : function()
11735     {
11736         this.el.setXY([0,0]);
11737         this.el.removeClass('in');
11738         this.el.hide();
11739         
11740     }
11741     
11742 });
11743
11744 Roo.bootstrap.Popover.alignment = {
11745     'left' : ['r-l', [-10,0], 'right'],
11746     'right' : ['l-r', [10,0], 'left'],
11747     'bottom' : ['t-b', [0,10], 'top'],
11748     'top' : [ 'b-t', [0,-10], 'bottom']
11749 };
11750
11751  /*
11752  * - LGPL
11753  *
11754  * Progress
11755  * 
11756  */
11757
11758 /**
11759  * @class Roo.bootstrap.Progress
11760  * @extends Roo.bootstrap.Component
11761  * Bootstrap Progress class
11762  * @cfg {Boolean} striped striped of the progress bar
11763  * @cfg {Boolean} active animated of the progress bar
11764  * 
11765  * 
11766  * @constructor
11767  * Create a new Progress
11768  * @param {Object} config The config object
11769  */
11770
11771 Roo.bootstrap.Progress = function(config){
11772     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11773 };
11774
11775 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
11776     
11777     striped : false,
11778     active: false,
11779     
11780     getAutoCreate : function(){
11781         var cfg = {
11782             tag: 'div',
11783             cls: 'progress'
11784         };
11785         
11786         
11787         if(this.striped){
11788             cfg.cls += ' progress-striped';
11789         }
11790       
11791         if(this.active){
11792             cfg.cls += ' active';
11793         }
11794         
11795         
11796         return cfg;
11797     }
11798    
11799 });
11800
11801  
11802
11803  /*
11804  * - LGPL
11805  *
11806  * ProgressBar
11807  * 
11808  */
11809
11810 /**
11811  * @class Roo.bootstrap.ProgressBar
11812  * @extends Roo.bootstrap.Component
11813  * Bootstrap ProgressBar class
11814  * @cfg {Number} aria_valuenow aria-value now
11815  * @cfg {Number} aria_valuemin aria-value min
11816  * @cfg {Number} aria_valuemax aria-value max
11817  * @cfg {String} label label for the progress bar
11818  * @cfg {String} panel (success | info | warning | danger )
11819  * @cfg {String} role role of the progress bar
11820  * @cfg {String} sr_only text
11821  * 
11822  * 
11823  * @constructor
11824  * Create a new ProgressBar
11825  * @param {Object} config The config object
11826  */
11827
11828 Roo.bootstrap.ProgressBar = function(config){
11829     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11830 };
11831
11832 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
11833     
11834     aria_valuenow : 0,
11835     aria_valuemin : 0,
11836     aria_valuemax : 100,
11837     label : false,
11838     panel : false,
11839     role : false,
11840     sr_only: false,
11841     
11842     getAutoCreate : function()
11843     {
11844         
11845         var cfg = {
11846             tag: 'div',
11847             cls: 'progress-bar',
11848             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11849         };
11850         
11851         if(this.sr_only){
11852             cfg.cn = {
11853                 tag: 'span',
11854                 cls: 'sr-only',
11855                 html: this.sr_only
11856             }
11857         }
11858         
11859         if(this.role){
11860             cfg.role = this.role;
11861         }
11862         
11863         if(this.aria_valuenow){
11864             cfg['aria-valuenow'] = this.aria_valuenow;
11865         }
11866         
11867         if(this.aria_valuemin){
11868             cfg['aria-valuemin'] = this.aria_valuemin;
11869         }
11870         
11871         if(this.aria_valuemax){
11872             cfg['aria-valuemax'] = this.aria_valuemax;
11873         }
11874         
11875         if(this.label && !this.sr_only){
11876             cfg.html = this.label;
11877         }
11878         
11879         if(this.panel){
11880             cfg.cls += ' progress-bar-' + this.panel;
11881         }
11882         
11883         return cfg;
11884     },
11885     
11886     update : function(aria_valuenow)
11887     {
11888         this.aria_valuenow = aria_valuenow;
11889         
11890         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11891     }
11892    
11893 });
11894
11895  
11896
11897  /*
11898  * - LGPL
11899  *
11900  * TabPanel
11901  * 
11902  */
11903
11904 /**
11905  * @class Roo.bootstrap.TabPanel
11906  * @extends Roo.bootstrap.Component
11907  * Bootstrap TabPanel class
11908  * @cfg {Boolean} active panel active
11909  * @cfg {String} html panel content
11910  * @cfg {String} tabId tab relate id
11911  * @cfg {String} navId The navbar which triggers show hide
11912  * 
11913  * 
11914  * @constructor
11915  * Create a new TabPanel
11916  * @param {Object} config The config object
11917  */
11918
11919 Roo.bootstrap.TabPanel = function(config){
11920     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11921      this.addEvents({
11922         /**
11923              * @event changed
11924              * Fires when the active status changes
11925              * @param {Roo.bootstrap.TabPanel} this
11926              * @param {Boolean} state the new state
11927             
11928          */
11929         'changed': true
11930      });
11931 };
11932
11933 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
11934     
11935     active: false,
11936     html: false,
11937     tabId: false,
11938     navId : false,
11939     
11940     getAutoCreate : function(){
11941         var cfg = {
11942             tag: 'div',
11943             cls: 'tab-pane',
11944             html: this.html || ''
11945         };
11946         
11947         if(this.active){
11948             cfg.cls += ' active';
11949         }
11950         
11951         if(this.tabId){
11952             cfg.tabId = this.tabId;
11953         }
11954         
11955         return cfg;
11956     },
11957     onRender : function(ct, position)
11958     {
11959        // Roo.log("Call onRender: " + this.xtype);
11960         
11961         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11962         
11963         if (this.navId && this.tabId) {
11964             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11965             if (!item) {
11966                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
11967             } else {
11968                 item.on('changed', function(item, state) {
11969                     this.setActive(state);
11970                 }, this);
11971             }
11972         }
11973         
11974     },
11975     setActive: function(state)
11976     {
11977         Roo.log("panel - set active " + this.tabId + "=" + state);
11978         
11979         this.active = state;
11980         if (!state) {
11981             this.el.removeClass('active');
11982             
11983         } else  if (!this.el.hasClass('active')) {
11984             this.el.addClass('active');
11985         }
11986         this.fireEvent('changed', this, state);
11987     }
11988     
11989     
11990 });
11991  
11992
11993  
11994
11995  /*
11996  * - LGPL
11997  *
11998  * DateField
11999  * 
12000  */
12001
12002 /**
12003  * @class Roo.bootstrap.DateField
12004  * @extends Roo.bootstrap.Input
12005  * Bootstrap DateField class
12006  * @cfg {Number} weekStart default 0
12007  * @cfg {Number} weekStart default 0
12008  * @cfg {Number} viewMode default empty, (months|years)
12009  * @cfg {Number} minViewMode default empty, (months|years)
12010  * @cfg {Number} startDate default -Infinity
12011  * @cfg {Number} endDate default Infinity
12012  * @cfg {Boolean} todayHighlight default false
12013  * @cfg {Boolean} todayBtn default false
12014  * @cfg {Boolean} calendarWeeks default false
12015  * @cfg {Object} daysOfWeekDisabled default empty
12016  * 
12017  * @cfg {Boolean} keyboardNavigation default true
12018  * @cfg {String} language default en
12019  * 
12020  * @constructor
12021  * Create a new DateField
12022  * @param {Object} config The config object
12023  */
12024
12025 Roo.bootstrap.DateField = function(config){
12026     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12027      this.addEvents({
12028             /**
12029              * @event show
12030              * Fires when this field show.
12031              * @param {Roo.bootstrap.DateField} this
12032              * @param {Mixed} date The date value
12033              */
12034             show : true,
12035             /**
12036              * @event show
12037              * Fires when this field hide.
12038              * @param {Roo.bootstrap.DateField} this
12039              * @param {Mixed} date The date value
12040              */
12041             hide : true,
12042             /**
12043              * @event select
12044              * Fires when select a date.
12045              * @param {Roo.bootstrap.DateField} this
12046              * @param {Mixed} date The date value
12047              */
12048             select : true
12049         });
12050 };
12051
12052 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12053     
12054     /**
12055      * @cfg {String} format
12056      * The default date format string which can be overriden for localization support.  The format must be
12057      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12058      */
12059     format : "m/d/y",
12060     /**
12061      * @cfg {String} altFormats
12062      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12063      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12064      */
12065     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12066     
12067     weekStart : 0,
12068     
12069     viewMode : '',
12070     
12071     minViewMode : '',
12072     
12073     todayHighlight : false,
12074     
12075     todayBtn: false,
12076     
12077     language: 'en',
12078     
12079     keyboardNavigation: true,
12080     
12081     calendarWeeks: false,
12082     
12083     startDate: -Infinity,
12084     
12085     endDate: Infinity,
12086     
12087     daysOfWeekDisabled: [],
12088     
12089     _events: [],
12090     
12091     UTCDate: function()
12092     {
12093         return new Date(Date.UTC.apply(Date, arguments));
12094     },
12095     
12096     UTCToday: function()
12097     {
12098         var today = new Date();
12099         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12100     },
12101     
12102     getDate: function() {
12103             var d = this.getUTCDate();
12104             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12105     },
12106     
12107     getUTCDate: function() {
12108             return this.date;
12109     },
12110     
12111     setDate: function(d) {
12112             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12113     },
12114     
12115     setUTCDate: function(d) {
12116             this.date = d;
12117             this.setValue(this.formatDate(this.date));
12118     },
12119         
12120     onRender: function(ct, position)
12121     {
12122         
12123         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12124         
12125         this.language = this.language || 'en';
12126         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12127         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12128         
12129         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12130         this.format = this.format || 'm/d/y';
12131         this.isInline = false;
12132         this.isInput = true;
12133         this.component = this.el.select('.add-on', true).first() || false;
12134         this.component = (this.component && this.component.length === 0) ? false : this.component;
12135         this.hasInput = this.component && this.inputEL().length;
12136         
12137         if (typeof(this.minViewMode === 'string')) {
12138             switch (this.minViewMode) {
12139                 case 'months':
12140                     this.minViewMode = 1;
12141                     break;
12142                 case 'years':
12143                     this.minViewMode = 2;
12144                     break;
12145                 default:
12146                     this.minViewMode = 0;
12147                     break;
12148             }
12149         }
12150         
12151         if (typeof(this.viewMode === 'string')) {
12152             switch (this.viewMode) {
12153                 case 'months':
12154                     this.viewMode = 1;
12155                     break;
12156                 case 'years':
12157                     this.viewMode = 2;
12158                     break;
12159                 default:
12160                     this.viewMode = 0;
12161                     break;
12162             }
12163         }
12164                 
12165         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12166         
12167         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12168         
12169         this.picker().on('mousedown', this.onMousedown, this);
12170         this.picker().on('click', this.onClick, this);
12171         
12172         this.picker().addClass('datepicker-dropdown');
12173         
12174         this.startViewMode = this.viewMode;
12175         
12176         
12177         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12178             if(!this.calendarWeeks){
12179                 v.remove();
12180                 return;
12181             };
12182             
12183             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12184             v.attr('colspan', function(i, val){
12185                 return parseInt(val) + 1;
12186             });
12187         })
12188                         
12189         
12190         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12191         
12192         this.setStartDate(this.startDate);
12193         this.setEndDate(this.endDate);
12194         
12195         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12196         
12197         this.fillDow();
12198         this.fillMonths();
12199         this.update();
12200         this.showMode();
12201         
12202         if(this.isInline) {
12203             this.show();
12204         }
12205     },
12206     
12207     picker : function()
12208     {
12209         return this.el.select('.datepicker', true).first();
12210     },
12211     
12212     fillDow: function()
12213     {
12214         var dowCnt = this.weekStart;
12215         
12216         var dow = {
12217             tag: 'tr',
12218             cn: [
12219                 
12220             ]
12221         };
12222         
12223         if(this.calendarWeeks){
12224             dow.cn.push({
12225                 tag: 'th',
12226                 cls: 'cw',
12227                 html: '&nbsp;'
12228             })
12229         }
12230         
12231         while (dowCnt < this.weekStart + 7) {
12232             dow.cn.push({
12233                 tag: 'th',
12234                 cls: 'dow',
12235                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12236             });
12237         }
12238         
12239         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12240     },
12241     
12242     fillMonths: function()
12243     {    
12244         var i = 0
12245         var months = this.picker().select('>.datepicker-months td', true).first();
12246         
12247         months.dom.innerHTML = '';
12248         
12249         while (i < 12) {
12250             var month = {
12251                 tag: 'span',
12252                 cls: 'month',
12253                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12254             }
12255             
12256             months.createChild(month);
12257         }
12258         
12259     },
12260     
12261     update: function(){
12262         
12263         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12264         
12265         if (this.date < this.startDate) {
12266             this.viewDate = new Date(this.startDate);
12267         } else if (this.date > this.endDate) {
12268             this.viewDate = new Date(this.endDate);
12269         } else {
12270             this.viewDate = new Date(this.date);
12271         }
12272         
12273         this.fill();
12274     },
12275     
12276     fill: function() {
12277         var d = new Date(this.viewDate),
12278                 year = d.getUTCFullYear(),
12279                 month = d.getUTCMonth(),
12280                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12281                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12282                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12283                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12284                 currentDate = this.date && this.date.valueOf(),
12285                 today = this.UTCToday();
12286         
12287         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12288         
12289 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12290         
12291 //        this.picker.select('>tfoot th.today').
12292 //                                              .text(dates[this.language].today)
12293 //                                              .toggle(this.todayBtn !== false);
12294     
12295         this.updateNavArrows();
12296         this.fillMonths();
12297                                                 
12298         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12299         
12300         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12301          
12302         prevMonth.setUTCDate(day);
12303         
12304         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12305         
12306         var nextMonth = new Date(prevMonth);
12307         
12308         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12309         
12310         nextMonth = nextMonth.valueOf();
12311         
12312         var fillMonths = false;
12313         
12314         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12315         
12316         while(prevMonth.valueOf() < nextMonth) {
12317             var clsName = '';
12318             
12319             if (prevMonth.getUTCDay() === this.weekStart) {
12320                 if(fillMonths){
12321                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12322                 }
12323                     
12324                 fillMonths = {
12325                     tag: 'tr',
12326                     cn: []
12327                 };
12328                 
12329                 if(this.calendarWeeks){
12330                     // ISO 8601: First week contains first thursday.
12331                     // ISO also states week starts on Monday, but we can be more abstract here.
12332                     var
12333                     // Start of current week: based on weekstart/current date
12334                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12335                     // Thursday of this week
12336                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12337                     // First Thursday of year, year from thursday
12338                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12339                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12340                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12341                     
12342                     fillMonths.cn.push({
12343                         tag: 'td',
12344                         cls: 'cw',
12345                         html: calWeek
12346                     });
12347                 }
12348             }
12349             
12350             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12351                 clsName += ' old';
12352             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12353                 clsName += ' new';
12354             }
12355             if (this.todayHighlight &&
12356                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12357                 prevMonth.getUTCMonth() == today.getMonth() &&
12358                 prevMonth.getUTCDate() == today.getDate()) {
12359                 clsName += ' today';
12360             }
12361             
12362             if (currentDate && prevMonth.valueOf() === currentDate) {
12363                 clsName += ' active';
12364             }
12365             
12366             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12367                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12368                     clsName += ' disabled';
12369             }
12370             
12371             fillMonths.cn.push({
12372                 tag: 'td',
12373                 cls: 'day ' + clsName,
12374                 html: prevMonth.getDate()
12375             })
12376             
12377             prevMonth.setDate(prevMonth.getDate()+1);
12378         }
12379           
12380         var currentYear = this.date && this.date.getUTCFullYear();
12381         var currentMonth = this.date && this.date.getUTCMonth();
12382         
12383         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12384         
12385         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12386             v.removeClass('active');
12387             
12388             if(currentYear === year && k === currentMonth){
12389                 v.addClass('active');
12390             }
12391             
12392             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12393                 v.addClass('disabled');
12394             }
12395             
12396         });
12397         
12398         
12399         year = parseInt(year/10, 10) * 10;
12400         
12401         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12402         
12403         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12404         
12405         year -= 1;
12406         for (var i = -1; i < 11; i++) {
12407             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12408                 tag: 'span',
12409                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12410                 html: year
12411             })
12412             
12413             year += 1;
12414         }
12415     },
12416     
12417     showMode: function(dir) {
12418         if (dir) {
12419             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12420         }
12421         Roo.each(this.picker().select('>div',true).elements, function(v){
12422             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12423             v.hide();
12424         });
12425         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12426     },
12427     
12428     place: function()
12429     {
12430         if(this.isInline) return;
12431         
12432         this.picker().removeClass(['bottom', 'top']);
12433         
12434         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12435             /*
12436              * place to the top of element!
12437              *
12438              */
12439             
12440             this.picker().addClass('top');
12441             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12442             
12443             return;
12444         }
12445         
12446         this.picker().addClass('bottom');
12447         
12448         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12449     },
12450     
12451     parseDate : function(value){
12452         if(!value || value instanceof Date){
12453             return value;
12454         }
12455         var v = Date.parseDate(value, this.format);
12456         if (!v && this.useIso) {
12457             v = Date.parseDate(value, 'Y-m-d');
12458         }
12459         if(!v && this.altFormats){
12460             if(!this.altFormatsArray){
12461                 this.altFormatsArray = this.altFormats.split("|");
12462             }
12463             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12464                 v = Date.parseDate(value, this.altFormatsArray[i]);
12465             }
12466         }
12467         return v;
12468     },
12469     
12470     formatDate : function(date, fmt){
12471         return (!date || !(date instanceof Date)) ?
12472         date : date.dateFormat(fmt || this.format);
12473     },
12474     
12475     onFocus : function()
12476     {
12477         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12478         this.show();
12479     },
12480     
12481     onBlur : function()
12482     {
12483         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12484         this.hide();
12485     },
12486     
12487     show : function()
12488     {
12489         this.picker().show();
12490         this.update();
12491         this.place();
12492         
12493         this.fireEvent('show', this, this.date);
12494     },
12495     
12496     hide : function()
12497     {
12498         if(this.isInline) return;
12499         this.picker().hide();
12500         this.viewMode = this.startViewMode;
12501         this.showMode();
12502         
12503         this.fireEvent('hide', this, this.date);
12504         
12505     },
12506     
12507     onMousedown: function(e){
12508         e.stopPropagation();
12509         e.preventDefault();
12510     },
12511     
12512     keyup: function(e){
12513         Roo.bootstrap.DateField.superclass.keyup.call(this);
12514         this.update();
12515         
12516     },
12517
12518     setValue: function(v){
12519         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12520         
12521         this.fireEvent('select', this, this.date);
12522         
12523     },
12524     
12525     fireKey: function(e){
12526         if (!this.picker().isVisible()){
12527             if (e.keyCode == 27) // allow escape to hide and re-show picker
12528                 this.show();
12529             return;
12530         }
12531         var dateChanged = false,
12532         dir, day, month,
12533         newDate, newViewDate;
12534         switch(e.keyCode){
12535             case 27: // escape
12536                 this.hide();
12537                 e.preventDefault();
12538                 break;
12539             case 37: // left
12540             case 39: // right
12541                 if (!this.keyboardNavigation) break;
12542                 dir = e.keyCode == 37 ? -1 : 1;
12543                 
12544                 if (e.ctrlKey){
12545                     newDate = this.moveYear(this.date, dir);
12546                     newViewDate = this.moveYear(this.viewDate, dir);
12547                 } else if (e.shiftKey){
12548                     newDate = this.moveMonth(this.date, dir);
12549                     newViewDate = this.moveMonth(this.viewDate, dir);
12550                 } else {
12551                     newDate = new Date(this.date);
12552                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12553                     newViewDate = new Date(this.viewDate);
12554                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12555                 }
12556                 if (this.dateWithinRange(newDate)){
12557                     this.date = newDate;
12558                     this.viewDate = newViewDate;
12559                     this.setValue(this.formatDate(this.date));
12560                     this.update();
12561                     e.preventDefault();
12562                     dateChanged = true;
12563                 }
12564                 break;
12565             case 38: // up
12566             case 40: // down
12567                 if (!this.keyboardNavigation) break;
12568                 dir = e.keyCode == 38 ? -1 : 1;
12569                 if (e.ctrlKey){
12570                     newDate = this.moveYear(this.date, dir);
12571                     newViewDate = this.moveYear(this.viewDate, dir);
12572                 } else if (e.shiftKey){
12573                     newDate = this.moveMonth(this.date, dir);
12574                     newViewDate = this.moveMonth(this.viewDate, dir);
12575                 } else {
12576                     newDate = new Date(this.date);
12577                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12578                     newViewDate = new Date(this.viewDate);
12579                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12580                 }
12581                 if (this.dateWithinRange(newDate)){
12582                     this.date = newDate;
12583                     this.viewDate = newViewDate;
12584                     this.setValue(this.formatDate(this.date));
12585                     this.update();
12586                     e.preventDefault();
12587                     dateChanged = true;
12588                 }
12589                 break;
12590             case 13: // enter
12591                 this.setValue(this.formatDate(this.date));
12592                 this.hide();
12593                 e.preventDefault();
12594                 break;
12595             case 9: // tab
12596                 this.setValue(this.formatDate(this.date));
12597                 this.hide();
12598                 break;
12599         }
12600     },
12601     
12602     
12603     onClick: function(e) {
12604         e.stopPropagation();
12605         e.preventDefault();
12606         
12607         var target = e.getTarget();
12608         
12609         if(target.nodeName.toLowerCase() === 'i'){
12610             target = Roo.get(target).dom.parentNode;
12611         }
12612         
12613         var nodeName = target.nodeName;
12614         var className = target.className;
12615         var html = target.innerHTML;
12616         
12617         switch(nodeName.toLowerCase()) {
12618             case 'th':
12619                 switch(className) {
12620                     case 'switch':
12621                         this.showMode(1);
12622                         break;
12623                     case 'prev':
12624                     case 'next':
12625                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12626                         switch(this.viewMode){
12627                                 case 0:
12628                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12629                                         break;
12630                                 case 1:
12631                                 case 2:
12632                                         this.viewDate = this.moveYear(this.viewDate, dir);
12633                                         break;
12634                         }
12635                         this.fill();
12636                         break;
12637                     case 'today':
12638                         var date = new Date();
12639                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12640                         this.fill()
12641                         this.setValue(this.formatDate(this.date));
12642                         this.hide();
12643                         break;
12644                 }
12645                 break;
12646             case 'span':
12647                 if (className.indexOf('disabled') === -1) {
12648                     this.viewDate.setUTCDate(1);
12649                     if (className.indexOf('month') !== -1) {
12650                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12651                     } else {
12652                         var year = parseInt(html, 10) || 0;
12653                         this.viewDate.setUTCFullYear(year);
12654                         
12655                     }
12656                     this.showMode(-1);
12657                     this.fill();
12658                 }
12659                 break;
12660                 
12661             case 'td':
12662                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12663                     var day = parseInt(html, 10) || 1;
12664                     var year = this.viewDate.getUTCFullYear(),
12665                         month = this.viewDate.getUTCMonth();
12666
12667                     if (className.indexOf('old') !== -1) {
12668                         if(month === 0 ){
12669                             month = 11;
12670                             year -= 1;
12671                         }else{
12672                             month -= 1;
12673                         }
12674                     } else if (className.indexOf('new') !== -1) {
12675                         if (month == 11) {
12676                             month = 0;
12677                             year += 1;
12678                         } else {
12679                             month += 1;
12680                         }
12681                     }
12682                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12683                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12684                     this.fill();
12685                     this.setValue(this.formatDate(this.date));
12686                     this.hide();
12687                 }
12688                 break;
12689         }
12690     },
12691     
12692     setStartDate: function(startDate){
12693         this.startDate = startDate || -Infinity;
12694         if (this.startDate !== -Infinity) {
12695             this.startDate = this.parseDate(this.startDate);
12696         }
12697         this.update();
12698         this.updateNavArrows();
12699     },
12700
12701     setEndDate: function(endDate){
12702         this.endDate = endDate || Infinity;
12703         if (this.endDate !== Infinity) {
12704             this.endDate = this.parseDate(this.endDate);
12705         }
12706         this.update();
12707         this.updateNavArrows();
12708     },
12709     
12710     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12711         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12712         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12713             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12714         }
12715         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12716             return parseInt(d, 10);
12717         });
12718         this.update();
12719         this.updateNavArrows();
12720     },
12721     
12722     updateNavArrows: function() {
12723         var d = new Date(this.viewDate),
12724         year = d.getUTCFullYear(),
12725         month = d.getUTCMonth();
12726         
12727         Roo.each(this.picker().select('.prev', true).elements, function(v){
12728             v.show();
12729             switch (this.viewMode) {
12730                 case 0:
12731
12732                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12733                         v.hide();
12734                     }
12735                     break;
12736                 case 1:
12737                 case 2:
12738                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12739                         v.hide();
12740                     }
12741                     break;
12742             }
12743         });
12744         
12745         Roo.each(this.picker().select('.next', true).elements, function(v){
12746             v.show();
12747             switch (this.viewMode) {
12748                 case 0:
12749
12750                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12751                         v.hide();
12752                     }
12753                     break;
12754                 case 1:
12755                 case 2:
12756                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12757                         v.hide();
12758                     }
12759                     break;
12760             }
12761         })
12762     },
12763     
12764     moveMonth: function(date, dir){
12765         if (!dir) return date;
12766         var new_date = new Date(date.valueOf()),
12767         day = new_date.getUTCDate(),
12768         month = new_date.getUTCMonth(),
12769         mag = Math.abs(dir),
12770         new_month, test;
12771         dir = dir > 0 ? 1 : -1;
12772         if (mag == 1){
12773             test = dir == -1
12774             // If going back one month, make sure month is not current month
12775             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12776             ? function(){
12777                 return new_date.getUTCMonth() == month;
12778             }
12779             // If going forward one month, make sure month is as expected
12780             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12781             : function(){
12782                 return new_date.getUTCMonth() != new_month;
12783             };
12784             new_month = month + dir;
12785             new_date.setUTCMonth(new_month);
12786             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12787             if (new_month < 0 || new_month > 11)
12788                 new_month = (new_month + 12) % 12;
12789         } else {
12790             // For magnitudes >1, move one month at a time...
12791             for (var i=0; i<mag; i++)
12792                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12793                 new_date = this.moveMonth(new_date, dir);
12794             // ...then reset the day, keeping it in the new month
12795             new_month = new_date.getUTCMonth();
12796             new_date.setUTCDate(day);
12797             test = function(){
12798                 return new_month != new_date.getUTCMonth();
12799             };
12800         }
12801         // Common date-resetting loop -- if date is beyond end of month, make it
12802         // end of month
12803         while (test()){
12804             new_date.setUTCDate(--day);
12805             new_date.setUTCMonth(new_month);
12806         }
12807         return new_date;
12808     },
12809
12810     moveYear: function(date, dir){
12811         return this.moveMonth(date, dir*12);
12812     },
12813
12814     dateWithinRange: function(date){
12815         return date >= this.startDate && date <= this.endDate;
12816     },
12817
12818     
12819     remove: function() {
12820         this.picker().remove();
12821     }
12822    
12823 });
12824
12825 Roo.apply(Roo.bootstrap.DateField,  {
12826     
12827     head : {
12828         tag: 'thead',
12829         cn: [
12830         {
12831             tag: 'tr',
12832             cn: [
12833             {
12834                 tag: 'th',
12835                 cls: 'prev',
12836                 html: '<i class="icon-arrow-left"/>'
12837             },
12838             {
12839                 tag: 'th',
12840                 cls: 'switch',
12841                 colspan: '5'
12842             },
12843             {
12844                 tag: 'th',
12845                 cls: 'next',
12846                 html: '<i class="icon-arrow-right"/>'
12847             }
12848
12849             ]
12850         }
12851         ]
12852     },
12853     
12854     content : {
12855         tag: 'tbody',
12856         cn: [
12857         {
12858             tag: 'tr',
12859             cn: [
12860             {
12861                 tag: 'td',
12862                 colspan: '7'
12863             }
12864             ]
12865         }
12866         ]
12867     },
12868     
12869     footer : {
12870         tag: 'tfoot',
12871         cn: [
12872         {
12873             tag: 'tr',
12874             cn: [
12875             {
12876                 tag: 'th',
12877                 colspan: '7',
12878                 cls: 'today'
12879             }
12880                     
12881             ]
12882         }
12883         ]
12884     },
12885     
12886     dates:{
12887         en: {
12888             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12889             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12890             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12891             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12892             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12893             today: "Today"
12894         }
12895     },
12896     
12897     modes: [
12898     {
12899         clsName: 'days',
12900         navFnc: 'Month',
12901         navStep: 1
12902     },
12903     {
12904         clsName: 'months',
12905         navFnc: 'FullYear',
12906         navStep: 1
12907     },
12908     {
12909         clsName: 'years',
12910         navFnc: 'FullYear',
12911         navStep: 10
12912     }]
12913 });
12914
12915 Roo.apply(Roo.bootstrap.DateField,  {
12916   
12917     template : {
12918         tag: 'div',
12919         cls: 'datepicker dropdown-menu',
12920         cn: [
12921         {
12922             tag: 'div',
12923             cls: 'datepicker-days',
12924             cn: [
12925             {
12926                 tag: 'table',
12927                 cls: 'table-condensed',
12928                 cn:[
12929                 Roo.bootstrap.DateField.head,
12930                 {
12931                     tag: 'tbody'
12932                 },
12933                 Roo.bootstrap.DateField.footer
12934                 ]
12935             }
12936             ]
12937         },
12938         {
12939             tag: 'div',
12940             cls: 'datepicker-months',
12941             cn: [
12942             {
12943                 tag: 'table',
12944                 cls: 'table-condensed',
12945                 cn:[
12946                 Roo.bootstrap.DateField.head,
12947                 Roo.bootstrap.DateField.content,
12948                 Roo.bootstrap.DateField.footer
12949                 ]
12950             }
12951             ]
12952         },
12953         {
12954             tag: 'div',
12955             cls: 'datepicker-years',
12956             cn: [
12957             {
12958                 tag: 'table',
12959                 cls: 'table-condensed',
12960                 cn:[
12961                 Roo.bootstrap.DateField.head,
12962                 Roo.bootstrap.DateField.content,
12963                 Roo.bootstrap.DateField.footer
12964                 ]
12965             }
12966             ]
12967         }
12968         ]
12969     }
12970 });
12971
12972  
12973
12974  /*
12975  * - LGPL
12976  *
12977  * TimeField
12978  * 
12979  */
12980
12981 /**
12982  * @class Roo.bootstrap.TimeField
12983  * @extends Roo.bootstrap.Input
12984  * Bootstrap DateField class
12985  * 
12986  * 
12987  * @constructor
12988  * Create a new TimeField
12989  * @param {Object} config The config object
12990  */
12991
12992 Roo.bootstrap.TimeField = function(config){
12993     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12994     this.addEvents({
12995             /**
12996              * @event show
12997              * Fires when this field show.
12998              * @param {Roo.bootstrap.DateField} this
12999              * @param {Mixed} date The date value
13000              */
13001             show : true,
13002             /**
13003              * @event show
13004              * Fires when this field hide.
13005              * @param {Roo.bootstrap.DateField} this
13006              * @param {Mixed} date The date value
13007              */
13008             hide : true,
13009             /**
13010              * @event select
13011              * Fires when select a date.
13012              * @param {Roo.bootstrap.DateField} this
13013              * @param {Mixed} date The date value
13014              */
13015             select : true
13016         });
13017 };
13018
13019 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13020     
13021     /**
13022      * @cfg {String} format
13023      * The default time format string which can be overriden for localization support.  The format must be
13024      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13025      */
13026     format : "H:i",
13027        
13028     onRender: function(ct, position)
13029     {
13030         
13031         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13032                 
13033         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13034         
13035         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13036         
13037         this.pop = this.picker().select('>.datepicker-time',true).first();
13038         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13039         
13040         this.picker().on('mousedown', this.onMousedown, this);
13041         this.picker().on('click', this.onClick, this);
13042         
13043         this.picker().addClass('datepicker-dropdown');
13044     
13045         this.fillTime();
13046         this.update();
13047             
13048         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13049         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13050         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13051         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13052         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13053         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13054
13055     },
13056     
13057     fireKey: function(e){
13058         if (!this.picker().isVisible()){
13059             if (e.keyCode == 27) // allow escape to hide and re-show picker
13060                 this.show();
13061             return;
13062         }
13063
13064         e.preventDefault();
13065         
13066         switch(e.keyCode){
13067             case 27: // escape
13068                 this.hide();
13069                 break;
13070             case 37: // left
13071             case 39: // right
13072                 this.onTogglePeriod();
13073                 break;
13074             case 38: // up
13075                 this.onIncrementMinutes();
13076                 break;
13077             case 40: // down
13078                 this.onDecrementMinutes();
13079                 break;
13080             case 13: // enter
13081             case 9: // tab
13082                 this.setTime();
13083                 break;
13084         }
13085     },
13086     
13087     onClick: function(e) {
13088         e.stopPropagation();
13089         e.preventDefault();
13090     },
13091     
13092     picker : function()
13093     {
13094         return this.el.select('.datepicker', true).first();
13095     },
13096     
13097     fillTime: function()
13098     {    
13099         var time = this.pop.select('tbody', true).first();
13100         
13101         time.dom.innerHTML = '';
13102         
13103         time.createChild({
13104             tag: 'tr',
13105             cn: [
13106                 {
13107                     tag: 'td',
13108                     cn: [
13109                         {
13110                             tag: 'a',
13111                             href: '#',
13112                             cls: 'btn',
13113                             cn: [
13114                                 {
13115                                     tag: 'span',
13116                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13117                                 }
13118                             ]
13119                         } 
13120                     ]
13121                 },
13122                 {
13123                     tag: 'td',
13124                     cls: 'separator'
13125                 },
13126                 {
13127                     tag: 'td',
13128                     cn: [
13129                         {
13130                             tag: 'a',
13131                             href: '#',
13132                             cls: 'btn',
13133                             cn: [
13134                                 {
13135                                     tag: 'span',
13136                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13137                                 }
13138                             ]
13139                         }
13140                     ]
13141                 },
13142                 {
13143                     tag: 'td',
13144                     cls: 'separator'
13145                 }
13146             ]
13147         });
13148         
13149         time.createChild({
13150             tag: 'tr',
13151             cn: [
13152                 {
13153                     tag: 'td',
13154                     cn: [
13155                         {
13156                             tag: 'span',
13157                             cls: 'timepicker-hour',
13158                             html: '00'
13159                         }  
13160                     ]
13161                 },
13162                 {
13163                     tag: 'td',
13164                     cls: 'separator',
13165                     html: ':'
13166                 },
13167                 {
13168                     tag: 'td',
13169                     cn: [
13170                         {
13171                             tag: 'span',
13172                             cls: 'timepicker-minute',
13173                             html: '00'
13174                         }  
13175                     ]
13176                 },
13177                 {
13178                     tag: 'td',
13179                     cls: 'separator'
13180                 },
13181                 {
13182                     tag: 'td',
13183                     cn: [
13184                         {
13185                             tag: 'button',
13186                             type: 'button',
13187                             cls: 'btn btn-primary period',
13188                             html: 'AM'
13189                             
13190                         }
13191                     ]
13192                 }
13193             ]
13194         });
13195         
13196         time.createChild({
13197             tag: 'tr',
13198             cn: [
13199                 {
13200                     tag: 'td',
13201                     cn: [
13202                         {
13203                             tag: 'a',
13204                             href: '#',
13205                             cls: 'btn',
13206                             cn: [
13207                                 {
13208                                     tag: 'span',
13209                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13210                                 }
13211                             ]
13212                         }
13213                     ]
13214                 },
13215                 {
13216                     tag: 'td',
13217                     cls: 'separator'
13218                 },
13219                 {
13220                     tag: 'td',
13221                     cn: [
13222                         {
13223                             tag: 'a',
13224                             href: '#',
13225                             cls: 'btn',
13226                             cn: [
13227                                 {
13228                                     tag: 'span',
13229                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13230                                 }
13231                             ]
13232                         }
13233                     ]
13234                 },
13235                 {
13236                     tag: 'td',
13237                     cls: 'separator'
13238                 }
13239             ]
13240         });
13241         
13242     },
13243     
13244     update: function()
13245     {
13246         
13247         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13248         
13249         this.fill();
13250     },
13251     
13252     fill: function() 
13253     {
13254         var hours = this.time.getHours();
13255         var minutes = this.time.getMinutes();
13256         var period = 'AM';
13257         
13258         if(hours > 11){
13259             period = 'PM';
13260         }
13261         
13262         if(hours == 0){
13263             hours = 12;
13264         }
13265         
13266         
13267         if(hours > 12){
13268             hours = hours - 12;
13269         }
13270         
13271         if(hours < 10){
13272             hours = '0' + hours;
13273         }
13274         
13275         if(minutes < 10){
13276             minutes = '0' + minutes;
13277         }
13278         
13279         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13280         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13281         this.pop.select('button', true).first().dom.innerHTML = period;
13282         
13283     },
13284     
13285     place: function()
13286     {   
13287         this.picker().removeClass(['bottom', 'top']);
13288         
13289         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13290             /*
13291              * place to the top of element!
13292              *
13293              */
13294             
13295             this.picker().addClass('top');
13296             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13297             
13298             return;
13299         }
13300         
13301         this.picker().addClass('bottom');
13302         
13303         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13304     },
13305   
13306     onFocus : function()
13307     {
13308         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13309         this.show();
13310     },
13311     
13312     onBlur : function()
13313     {
13314         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13315         this.hide();
13316     },
13317     
13318     show : function()
13319     {
13320         this.picker().show();
13321         this.pop.show();
13322         this.update();
13323         this.place();
13324         
13325         this.fireEvent('show', this, this.date);
13326     },
13327     
13328     hide : function()
13329     {
13330         this.picker().hide();
13331         this.pop.hide();
13332         
13333         this.fireEvent('hide', this, this.date);
13334     },
13335     
13336     setTime : function()
13337     {
13338         this.hide();
13339         this.setValue(this.time.format(this.format));
13340         
13341         this.fireEvent('select', this, this.date);
13342         
13343         
13344     },
13345     
13346     onMousedown: function(e){
13347         e.stopPropagation();
13348         e.preventDefault();
13349     },
13350     
13351     onIncrementHours: function()
13352     {
13353         Roo.log('onIncrementHours');
13354         this.time = this.time.add(Date.HOUR, 1);
13355         this.update();
13356         
13357     },
13358     
13359     onDecrementHours: function()
13360     {
13361         Roo.log('onDecrementHours');
13362         this.time = this.time.add(Date.HOUR, -1);
13363         this.update();
13364     },
13365     
13366     onIncrementMinutes: function()
13367     {
13368         Roo.log('onIncrementMinutes');
13369         this.time = this.time.add(Date.MINUTE, 1);
13370         this.update();
13371     },
13372     
13373     onDecrementMinutes: function()
13374     {
13375         Roo.log('onDecrementMinutes');
13376         this.time = this.time.add(Date.MINUTE, -1);
13377         this.update();
13378     },
13379     
13380     onTogglePeriod: function()
13381     {
13382         Roo.log('onTogglePeriod');
13383         this.time = this.time.add(Date.HOUR, 12);
13384         this.update();
13385     }
13386     
13387    
13388 });
13389
13390 Roo.apply(Roo.bootstrap.TimeField,  {
13391     
13392     content : {
13393         tag: 'tbody',
13394         cn: [
13395             {
13396                 tag: 'tr',
13397                 cn: [
13398                 {
13399                     tag: 'td',
13400                     colspan: '7'
13401                 }
13402                 ]
13403             }
13404         ]
13405     },
13406     
13407     footer : {
13408         tag: 'tfoot',
13409         cn: [
13410             {
13411                 tag: 'tr',
13412                 cn: [
13413                 {
13414                     tag: 'th',
13415                     colspan: '7',
13416                     cls: '',
13417                     cn: [
13418                         {
13419                             tag: 'button',
13420                             cls: 'btn btn-info ok',
13421                             html: 'OK'
13422                         }
13423                     ]
13424                 }
13425
13426                 ]
13427             }
13428         ]
13429     }
13430 });
13431
13432 Roo.apply(Roo.bootstrap.TimeField,  {
13433   
13434     template : {
13435         tag: 'div',
13436         cls: 'datepicker dropdown-menu',
13437         cn: [
13438             {
13439                 tag: 'div',
13440                 cls: 'datepicker-time',
13441                 cn: [
13442                 {
13443                     tag: 'table',
13444                     cls: 'table-condensed',
13445                     cn:[
13446                     Roo.bootstrap.TimeField.content,
13447                     Roo.bootstrap.TimeField.footer
13448                     ]
13449                 }
13450                 ]
13451             }
13452         ]
13453     }
13454 });
13455
13456  
13457
13458  /*
13459  * - LGPL
13460  *
13461  * CheckBox
13462  * 
13463  */
13464
13465 /**
13466  * @class Roo.bootstrap.CheckBox
13467  * @extends Roo.bootstrap.Input
13468  * Bootstrap CheckBox class
13469  * 
13470  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13471  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13472  * @cfg {String} boxLabel The text that appears beside the checkbox
13473  * @cfg {Boolean} checked initnal the element
13474  * 
13475  * @constructor
13476  * Create a new CheckBox
13477  * @param {Object} config The config object
13478  */
13479
13480 Roo.bootstrap.CheckBox = function(config){
13481     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13482    
13483         this.addEvents({
13484             /**
13485             * @event check
13486             * Fires when the element is checked or unchecked.
13487             * @param {Roo.bootstrap.CheckBox} this This input
13488             * @param {Boolean} checked The new checked value
13489             */
13490            check : true
13491         });
13492 };
13493
13494 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13495     
13496     inputType: 'checkbox',
13497     inputValue: 1,
13498     valueOff: 0,
13499     boxLabel: false,
13500     checked: false,
13501     
13502     getAutoCreate : function()
13503     {
13504         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13505         
13506         var id = Roo.id();
13507         
13508         var cfg = {};
13509         
13510         cfg.cls = 'form-group' //input-group
13511         
13512         var input =  {
13513             tag: 'input',
13514             id : id,
13515             type : this.inputType,
13516             value : (!this.checked) ? this.valueOff : this.inputValue,
13517             cls : 'form-box',
13518             placeholder : this.placeholder || ''
13519             
13520         };
13521         
13522         if (this.disabled) {
13523             input.disabled=true;
13524         }
13525         
13526         if(this.checked){
13527             input.checked = this.checked;
13528         }
13529         
13530         if (this.name) {
13531             input.name = this.name;
13532         }
13533         
13534         if (this.size) {
13535             input.cls += ' input-' + this.size;
13536         }
13537         
13538         var settings=this;
13539         ['xs','sm','md','lg'].map(function(size){
13540             if (settings[size]) {
13541                 cfg.cls += ' col-' + size + '-' + settings[size];
13542             }
13543         });
13544         
13545         var inputblock = input;
13546         
13547         if (this.before || this.after) {
13548             
13549             inputblock = {
13550                 cls : 'input-group',
13551                 cn :  [] 
13552             };
13553             if (this.before) {
13554                 inputblock.cn.push({
13555                     tag :'span',
13556                     cls : 'input-group-addon',
13557                     html : this.before
13558                 });
13559             }
13560             inputblock.cn.push(input);
13561             if (this.after) {
13562                 inputblock.cn.push({
13563                     tag :'span',
13564                     cls : 'input-group-addon',
13565                     html : this.after
13566                 });
13567             }
13568             
13569         };
13570         
13571         if (align ==='left' && this.fieldLabel.length) {
13572                 Roo.log("left and has label");
13573                 cfg.cn = [
13574                     
13575                     {
13576                         tag: 'label',
13577                         'for' :  id,
13578                         cls : 'control-label col-md-' + this.labelWidth,
13579                         html : this.fieldLabel
13580                         
13581                     },
13582                     {
13583                         cls : "col-md-" + (12 - this.labelWidth), 
13584                         cn: [
13585                             inputblock
13586                         ]
13587                     }
13588                     
13589                 ];
13590         } else if ( this.fieldLabel.length) {
13591                 Roo.log(" label");
13592                 cfg.cn = [
13593                    
13594                     {
13595                         tag: this.boxLabel ? 'span' : 'label',
13596                         'for': id,
13597                         cls: 'control-label box-input-label',
13598                         //cls : 'input-group-addon',
13599                         html : this.fieldLabel
13600                         
13601                     },
13602                     
13603                     inputblock
13604                     
13605                 ];
13606
13607         } else {
13608             
13609                    Roo.log(" no label && no align");
13610                 cfg.cn = [
13611                     
13612                         inputblock
13613                     
13614                 ];
13615                 
13616                 
13617         };
13618         
13619         if(this.boxLabel){
13620             cfg.cn.push({
13621                 tag: 'label',
13622                 'for': id,
13623                 cls: 'box-label',
13624                 html: this.boxLabel
13625             })
13626         }
13627         
13628         return cfg;
13629         
13630     },
13631     
13632     /**
13633      * return the real input element.
13634      */
13635     inputEl: function ()
13636     {
13637         return this.el.select('input.form-box',true).first();
13638     },
13639     
13640     label: function()
13641     {
13642         return this.el.select('label.control-label',true).first();
13643     },
13644     
13645     initEvents : function()
13646     {
13647 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13648         
13649         this.inputEl().on('click', this.onClick,  this);
13650         
13651     },
13652     
13653     onClick : function()
13654     {   
13655         this.setChecked(!this.checked);
13656     },
13657     
13658     setChecked : function(state,suppressEvent)
13659     {
13660         this.checked = state;
13661         
13662         this.inputEl().dom.checked = state;
13663         
13664         if(suppressEvent !== true){
13665             this.fireEvent('check', this, state);
13666         }
13667         
13668         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13669         
13670     },
13671     
13672     setValue : function(v,suppressEvent)
13673     {
13674         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13675     }
13676     
13677 });
13678
13679  
13680 /*
13681  * - LGPL
13682  *
13683  * Radio
13684  * 
13685  */
13686
13687 /**
13688  * @class Roo.bootstrap.Radio
13689  * @extends Roo.bootstrap.CheckBox
13690  * Bootstrap Radio class
13691
13692  * @constructor
13693  * Create a new Radio
13694  * @param {Object} config The config object
13695  */
13696
13697 Roo.bootstrap.Radio = function(config){
13698     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13699    
13700 };
13701
13702 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13703     
13704     inputType: 'radio',
13705     inputValue: '',
13706     valueOff: '',
13707     
13708     getAutoCreate : function()
13709     {
13710         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13711         
13712         var id = Roo.id();
13713         
13714         var cfg = {};
13715         
13716         cfg.cls = 'form-group' //input-group
13717         
13718         var input =  {
13719             tag: 'input',
13720             id : id,
13721             type : this.inputType,
13722             value : (!this.checked) ? this.valueOff : this.inputValue,
13723             cls : 'form-box',
13724             placeholder : this.placeholder || ''
13725             
13726         };
13727         
13728         if (this.disabled) {
13729             input.disabled=true;
13730         }
13731         
13732         if(this.checked){
13733             input.checked = this.checked;
13734         }
13735         
13736         if (this.name) {
13737             input.name = this.name;
13738         }
13739         
13740         if (this.size) {
13741             input.cls += ' input-' + this.size;
13742         }
13743         
13744         var settings=this;
13745         ['xs','sm','md','lg'].map(function(size){
13746             if (settings[size]) {
13747                 cfg.cls += ' col-' + size + '-' + settings[size];
13748             }
13749         });
13750         
13751         var inputblock = input;
13752         
13753         if (this.before || this.after) {
13754             
13755             inputblock = {
13756                 cls : 'input-group',
13757                 cn :  [] 
13758             };
13759             if (this.before) {
13760                 inputblock.cn.push({
13761                     tag :'span',
13762                     cls : 'input-group-addon',
13763                     html : this.before
13764                 });
13765             }
13766             inputblock.cn.push(input);
13767             if (this.after) {
13768                 inputblock.cn.push({
13769                     tag :'span',
13770                     cls : 'input-group-addon',
13771                     html : this.after
13772                 });
13773             }
13774             
13775         };
13776         
13777         if (align ==='left' && this.fieldLabel.length) {
13778                 Roo.log("left and has label");
13779                 cfg.cn = [
13780                     
13781                     {
13782                         tag: 'label',
13783                         'for' :  id,
13784                         cls : 'control-label col-md-' + this.labelWidth,
13785                         html : this.fieldLabel
13786                         
13787                     },
13788                     {
13789                         cls : "col-md-" + (12 - this.labelWidth), 
13790                         cn: [
13791                             inputblock
13792                         ]
13793                     }
13794                     
13795                 ];
13796         } else if ( this.fieldLabel.length) {
13797                 Roo.log(" label");
13798                  cfg.cn = [
13799                    
13800                     {
13801                         tag: 'label',
13802                         'for': id,
13803                         cls: 'control-label box-input-label',
13804                         //cls : 'input-group-addon',
13805                         html : this.fieldLabel
13806                         
13807                     },
13808                     
13809                     inputblock
13810                     
13811                 ];
13812
13813         } else {
13814             
13815                    Roo.log(" no label && no align");
13816                 cfg.cn = [
13817                     
13818                         inputblock
13819                     
13820                 ];
13821                 
13822                 
13823         };
13824         
13825         if(this.boxLabel){
13826             cfg.cn.push({
13827                 tag: 'label',
13828                 'for': id,
13829                 cls: 'box-label',
13830                 html: this.boxLabel
13831             })
13832         }
13833         
13834         return cfg;
13835         
13836     },
13837    
13838     onClick : function()
13839     {   
13840         this.setChecked(true);
13841     },
13842     
13843     setChecked : function(state,suppressEvent)
13844     {
13845         if(state){
13846             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13847                 v.dom.checked = false;
13848             });
13849         }
13850         
13851         this.checked = state;
13852         this.inputEl().dom.checked = state;
13853         
13854         if(suppressEvent !== true){
13855             this.fireEvent('check', this, state);
13856         }
13857         
13858         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13859         
13860     },
13861     
13862     getGroupValue : function()
13863     {
13864         var value = ''
13865         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13866             if(v.dom.checked == true){
13867                 value = v.dom.value;
13868             }
13869         });
13870         
13871         return value;
13872     },
13873     
13874     /**
13875      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13876      * @return {Mixed} value The field value
13877      */
13878     getValue : function(){
13879         return this.getGroupValue();
13880     }
13881     
13882 });
13883
13884  
13885 //<script type="text/javascript">
13886
13887 /*
13888  * Based  Ext JS Library 1.1.1
13889  * Copyright(c) 2006-2007, Ext JS, LLC.
13890  * LGPL
13891  *
13892  */
13893  
13894 /**
13895  * @class Roo.HtmlEditorCore
13896  * @extends Roo.Component
13897  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13898  *
13899  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13900  */
13901
13902 Roo.HtmlEditorCore = function(config){
13903     
13904     
13905     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13906     this.addEvents({
13907         /**
13908          * @event initialize
13909          * Fires when the editor is fully initialized (including the iframe)
13910          * @param {Roo.HtmlEditorCore} this
13911          */
13912         initialize: true,
13913         /**
13914          * @event activate
13915          * Fires when the editor is first receives the focus. Any insertion must wait
13916          * until after this event.
13917          * @param {Roo.HtmlEditorCore} this
13918          */
13919         activate: true,
13920          /**
13921          * @event beforesync
13922          * Fires before the textarea is updated with content from the editor iframe. Return false
13923          * to cancel the sync.
13924          * @param {Roo.HtmlEditorCore} this
13925          * @param {String} html
13926          */
13927         beforesync: true,
13928          /**
13929          * @event beforepush
13930          * Fires before the iframe editor is updated with content from the textarea. Return false
13931          * to cancel the push.
13932          * @param {Roo.HtmlEditorCore} this
13933          * @param {String} html
13934          */
13935         beforepush: true,
13936          /**
13937          * @event sync
13938          * Fires when the textarea is updated with content from the editor iframe.
13939          * @param {Roo.HtmlEditorCore} this
13940          * @param {String} html
13941          */
13942         sync: true,
13943          /**
13944          * @event push
13945          * Fires when the iframe editor is updated with content from the textarea.
13946          * @param {Roo.HtmlEditorCore} this
13947          * @param {String} html
13948          */
13949         push: true,
13950         
13951         /**
13952          * @event editorevent
13953          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13954          * @param {Roo.HtmlEditorCore} this
13955          */
13956         editorevent: true
13957     });
13958      
13959 };
13960
13961
13962 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
13963
13964
13965      /**
13966      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
13967      */
13968     
13969     owner : false,
13970     
13971      /**
13972      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13973      *                        Roo.resizable.
13974      */
13975     resizable : false,
13976      /**
13977      * @cfg {Number} height (in pixels)
13978      */   
13979     height: 300,
13980    /**
13981      * @cfg {Number} width (in pixels)
13982      */   
13983     width: 500,
13984     
13985     /**
13986      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13987      * 
13988      */
13989     stylesheets: false,
13990     
13991     // id of frame..
13992     frameId: false,
13993     
13994     // private properties
13995     validationEvent : false,
13996     deferHeight: true,
13997     initialized : false,
13998     activated : false,
13999     sourceEditMode : false,
14000     onFocus : Roo.emptyFn,
14001     iframePad:3,
14002     hideMode:'offsets',
14003     
14004     clearUp: true,
14005     
14006      
14007     
14008
14009     /**
14010      * Protected method that will not generally be called directly. It
14011      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14012      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14013      */
14014     getDocMarkup : function(){
14015         // body styles..
14016         var st = '';
14017         Roo.log(this.stylesheets);
14018         
14019         // inherit styels from page...?? 
14020         if (this.stylesheets === false) {
14021             
14022             Roo.get(document.head).select('style').each(function(node) {
14023                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14024             });
14025             
14026             Roo.get(document.head).select('link').each(function(node) { 
14027                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14028             });
14029             
14030         } else if (!this.stylesheets.length) {
14031                 // simple..
14032                 st = '<style type="text/css">' +
14033                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14034                    '</style>';
14035         } else {
14036             Roo.each(this.stylesheets, function(s) {
14037                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14038             });
14039             
14040         }
14041         
14042         st +=  '<style type="text/css">' +
14043             'IMG { cursor: pointer } ' +
14044         '</style>';
14045
14046         
14047         return '<html><head>' + st  +
14048             //<style type="text/css">' +
14049             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14050             //'</style>' +
14051             ' </head><body class="roo-htmleditor-body"></body></html>';
14052     },
14053
14054     // private
14055     onRender : function(ct, position)
14056     {
14057         var _t = this;
14058         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14059         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14060         
14061         
14062         this.el.dom.style.border = '0 none';
14063         this.el.dom.setAttribute('tabIndex', -1);
14064         this.el.addClass('x-hidden hide');
14065         
14066         
14067         
14068         if(Roo.isIE){ // fix IE 1px bogus margin
14069             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14070         }
14071        
14072         
14073         this.frameId = Roo.id();
14074         
14075          
14076         
14077         var iframe = this.owner.wrap.createChild({
14078             tag: 'iframe',
14079             cls: 'form-control', // bootstrap..
14080             id: this.frameId,
14081             name: this.frameId,
14082             frameBorder : 'no',
14083             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14084         }, this.el
14085         );
14086         
14087         
14088         this.iframe = iframe.dom;
14089
14090          this.assignDocWin();
14091         
14092         this.doc.designMode = 'on';
14093        
14094         this.doc.open();
14095         this.doc.write(this.getDocMarkup());
14096         this.doc.close();
14097
14098         
14099         var task = { // must defer to wait for browser to be ready
14100             run : function(){
14101                 //console.log("run task?" + this.doc.readyState);
14102                 this.assignDocWin();
14103                 if(this.doc.body || this.doc.readyState == 'complete'){
14104                     try {
14105                         this.doc.designMode="on";
14106                     } catch (e) {
14107                         return;
14108                     }
14109                     Roo.TaskMgr.stop(task);
14110                     this.initEditor.defer(10, this);
14111                 }
14112             },
14113             interval : 10,
14114             duration: 10000,
14115             scope: this
14116         };
14117         Roo.TaskMgr.start(task);
14118
14119         
14120          
14121     },
14122
14123     // private
14124     onResize : function(w, h)
14125     {
14126          Roo.log('resize: ' +w + ',' + h );
14127         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14128         if(!this.iframe){
14129             return;
14130         }
14131         if(typeof w == 'number'){
14132             
14133             this.iframe.style.width = w + 'px';
14134         }
14135         if(typeof h == 'number'){
14136             
14137             this.iframe.style.height = h + 'px';
14138             if(this.doc){
14139                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14140             }
14141         }
14142         
14143     },
14144
14145     /**
14146      * Toggles the editor between standard and source edit mode.
14147      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14148      */
14149     toggleSourceEdit : function(sourceEditMode){
14150         
14151         this.sourceEditMode = sourceEditMode === true;
14152         
14153         if(this.sourceEditMode){
14154  
14155             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14156             
14157         }else{
14158             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14159             //this.iframe.className = '';
14160             this.deferFocus();
14161         }
14162         //this.setSize(this.owner.wrap.getSize());
14163         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14164     },
14165
14166     
14167   
14168
14169     /**
14170      * Protected method that will not generally be called directly. If you need/want
14171      * custom HTML cleanup, this is the method you should override.
14172      * @param {String} html The HTML to be cleaned
14173      * return {String} The cleaned HTML
14174      */
14175     cleanHtml : function(html){
14176         html = String(html);
14177         if(html.length > 5){
14178             if(Roo.isSafari){ // strip safari nonsense
14179                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14180             }
14181         }
14182         if(html == '&nbsp;'){
14183             html = '';
14184         }
14185         return html;
14186     },
14187
14188     /**
14189      * HTML Editor -> Textarea
14190      * Protected method that will not generally be called directly. Syncs the contents
14191      * of the editor iframe with the textarea.
14192      */
14193     syncValue : function(){
14194         if(this.initialized){
14195             var bd = (this.doc.body || this.doc.documentElement);
14196             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14197             var html = bd.innerHTML;
14198             if(Roo.isSafari){
14199                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14200                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14201                 if(m && m[1]){
14202                     html = '<div style="'+m[0]+'">' + html + '</div>';
14203                 }
14204             }
14205             html = this.cleanHtml(html);
14206             // fix up the special chars.. normaly like back quotes in word...
14207             // however we do not want to do this with chinese..
14208             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14209                 var cc = b.charCodeAt();
14210                 if (
14211                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14212                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14213                     (cc >= 0xf900 && cc < 0xfb00 )
14214                 ) {
14215                         return b;
14216                 }
14217                 return "&#"+cc+";" 
14218             });
14219             if(this.owner.fireEvent('beforesync', this, html) !== false){
14220                 this.el.dom.value = html;
14221                 this.owner.fireEvent('sync', this, html);
14222             }
14223         }
14224     },
14225
14226     /**
14227      * Protected method that will not generally be called directly. Pushes the value of the textarea
14228      * into the iframe editor.
14229      */
14230     pushValue : function(){
14231         if(this.initialized){
14232             var v = this.el.dom.value.trim();
14233             
14234 //            if(v.length < 1){
14235 //                v = '&#160;';
14236 //            }
14237             
14238             if(this.owner.fireEvent('beforepush', this, v) !== false){
14239                 var d = (this.doc.body || this.doc.documentElement);
14240                 d.innerHTML = v;
14241                 this.cleanUpPaste();
14242                 this.el.dom.value = d.innerHTML;
14243                 this.owner.fireEvent('push', this, v);
14244             }
14245         }
14246     },
14247
14248     // private
14249     deferFocus : function(){
14250         this.focus.defer(10, this);
14251     },
14252
14253     // doc'ed in Field
14254     focus : function(){
14255         if(this.win && !this.sourceEditMode){
14256             this.win.focus();
14257         }else{
14258             this.el.focus();
14259         }
14260     },
14261     
14262     assignDocWin: function()
14263     {
14264         var iframe = this.iframe;
14265         
14266          if(Roo.isIE){
14267             this.doc = iframe.contentWindow.document;
14268             this.win = iframe.contentWindow;
14269         } else {
14270             if (!Roo.get(this.frameId)) {
14271                 return;
14272             }
14273             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14274             this.win = Roo.get(this.frameId).dom.contentWindow;
14275         }
14276     },
14277     
14278     // private
14279     initEditor : function(){
14280         //console.log("INIT EDITOR");
14281         this.assignDocWin();
14282         
14283         
14284         
14285         this.doc.designMode="on";
14286         this.doc.open();
14287         this.doc.write(this.getDocMarkup());
14288         this.doc.close();
14289         
14290         var dbody = (this.doc.body || this.doc.documentElement);
14291         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14292         // this copies styles from the containing element into thsi one..
14293         // not sure why we need all of this..
14294         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14295         ss['background-attachment'] = 'fixed'; // w3c
14296         dbody.bgProperties = 'fixed'; // ie
14297         Roo.DomHelper.applyStyles(dbody, ss);
14298         Roo.EventManager.on(this.doc, {
14299             //'mousedown': this.onEditorEvent,
14300             'mouseup': this.onEditorEvent,
14301             'dblclick': this.onEditorEvent,
14302             'click': this.onEditorEvent,
14303             'keyup': this.onEditorEvent,
14304             buffer:100,
14305             scope: this
14306         });
14307         if(Roo.isGecko){
14308             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14309         }
14310         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14311             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14312         }
14313         this.initialized = true;
14314
14315         this.owner.fireEvent('initialize', this);
14316         this.pushValue();
14317     },
14318
14319     // private
14320     onDestroy : function(){
14321         
14322         
14323         
14324         if(this.rendered){
14325             
14326             //for (var i =0; i < this.toolbars.length;i++) {
14327             //    // fixme - ask toolbars for heights?
14328             //    this.toolbars[i].onDestroy();
14329            // }
14330             
14331             //this.wrap.dom.innerHTML = '';
14332             //this.wrap.remove();
14333         }
14334     },
14335
14336     // private
14337     onFirstFocus : function(){
14338         
14339         this.assignDocWin();
14340         
14341         
14342         this.activated = true;
14343          
14344     
14345         if(Roo.isGecko){ // prevent silly gecko errors
14346             this.win.focus();
14347             var s = this.win.getSelection();
14348             if(!s.focusNode || s.focusNode.nodeType != 3){
14349                 var r = s.getRangeAt(0);
14350                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14351                 r.collapse(true);
14352                 this.deferFocus();
14353             }
14354             try{
14355                 this.execCmd('useCSS', true);
14356                 this.execCmd('styleWithCSS', false);
14357             }catch(e){}
14358         }
14359         this.owner.fireEvent('activate', this);
14360     },
14361
14362     // private
14363     adjustFont: function(btn){
14364         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14365         //if(Roo.isSafari){ // safari
14366         //    adjust *= 2;
14367        // }
14368         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14369         if(Roo.isSafari){ // safari
14370             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14371             v =  (v < 10) ? 10 : v;
14372             v =  (v > 48) ? 48 : v;
14373             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14374             
14375         }
14376         
14377         
14378         v = Math.max(1, v+adjust);
14379         
14380         this.execCmd('FontSize', v  );
14381     },
14382
14383     onEditorEvent : function(e){
14384         this.owner.fireEvent('editorevent', this, e);
14385       //  this.updateToolbar();
14386         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14387     },
14388
14389     insertTag : function(tg)
14390     {
14391         // could be a bit smarter... -> wrap the current selected tRoo..
14392         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14393             
14394             range = this.createRange(this.getSelection());
14395             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14396             wrappingNode.appendChild(range.extractContents());
14397             range.insertNode(wrappingNode);
14398
14399             return;
14400             
14401             
14402             
14403         }
14404         this.execCmd("formatblock",   tg);
14405         
14406     },
14407     
14408     insertText : function(txt)
14409     {
14410         
14411         
14412         var range = this.createRange();
14413         range.deleteContents();
14414                //alert(Sender.getAttribute('label'));
14415                
14416         range.insertNode(this.doc.createTextNode(txt));
14417     } ,
14418     
14419      
14420
14421     /**
14422      * Executes a Midas editor command on the editor document and performs necessary focus and
14423      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14424      * @param {String} cmd The Midas command
14425      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14426      */
14427     relayCmd : function(cmd, value){
14428         this.win.focus();
14429         this.execCmd(cmd, value);
14430         this.owner.fireEvent('editorevent', this);
14431         //this.updateToolbar();
14432         this.owner.deferFocus();
14433     },
14434
14435     /**
14436      * Executes a Midas editor command directly on the editor document.
14437      * For visual commands, you should use {@link #relayCmd} instead.
14438      * <b>This should only be called after the editor is initialized.</b>
14439      * @param {String} cmd The Midas command
14440      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14441      */
14442     execCmd : function(cmd, value){
14443         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14444         this.syncValue();
14445     },
14446  
14447  
14448    
14449     /**
14450      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14451      * to insert tRoo.
14452      * @param {String} text | dom node.. 
14453      */
14454     insertAtCursor : function(text)
14455     {
14456         
14457         
14458         
14459         if(!this.activated){
14460             return;
14461         }
14462         /*
14463         if(Roo.isIE){
14464             this.win.focus();
14465             var r = this.doc.selection.createRange();
14466             if(r){
14467                 r.collapse(true);
14468                 r.pasteHTML(text);
14469                 this.syncValue();
14470                 this.deferFocus();
14471             
14472             }
14473             return;
14474         }
14475         */
14476         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14477             this.win.focus();
14478             
14479             
14480             // from jquery ui (MIT licenced)
14481             var range, node;
14482             var win = this.win;
14483             
14484             if (win.getSelection && win.getSelection().getRangeAt) {
14485                 range = win.getSelection().getRangeAt(0);
14486                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14487                 range.insertNode(node);
14488             } else if (win.document.selection && win.document.selection.createRange) {
14489                 // no firefox support
14490                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14491                 win.document.selection.createRange().pasteHTML(txt);
14492             } else {
14493                 // no firefox support
14494                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14495                 this.execCmd('InsertHTML', txt);
14496             } 
14497             
14498             this.syncValue();
14499             
14500             this.deferFocus();
14501         }
14502     },
14503  // private
14504     mozKeyPress : function(e){
14505         if(e.ctrlKey){
14506             var c = e.getCharCode(), cmd;
14507           
14508             if(c > 0){
14509                 c = String.fromCharCode(c).toLowerCase();
14510                 switch(c){
14511                     case 'b':
14512                         cmd = 'bold';
14513                         break;
14514                     case 'i':
14515                         cmd = 'italic';
14516                         break;
14517                     
14518                     case 'u':
14519                         cmd = 'underline';
14520                         break;
14521                     
14522                     case 'v':
14523                         this.cleanUpPaste.defer(100, this);
14524                         return;
14525                         
14526                 }
14527                 if(cmd){
14528                     this.win.focus();
14529                     this.execCmd(cmd);
14530                     this.deferFocus();
14531                     e.preventDefault();
14532                 }
14533                 
14534             }
14535         }
14536     },
14537
14538     // private
14539     fixKeys : function(){ // load time branching for fastest keydown performance
14540         if(Roo.isIE){
14541             return function(e){
14542                 var k = e.getKey(), r;
14543                 if(k == e.TAB){
14544                     e.stopEvent();
14545                     r = this.doc.selection.createRange();
14546                     if(r){
14547                         r.collapse(true);
14548                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14549                         this.deferFocus();
14550                     }
14551                     return;
14552                 }
14553                 
14554                 if(k == e.ENTER){
14555                     r = this.doc.selection.createRange();
14556                     if(r){
14557                         var target = r.parentElement();
14558                         if(!target || target.tagName.toLowerCase() != 'li'){
14559                             e.stopEvent();
14560                             r.pasteHTML('<br />');
14561                             r.collapse(false);
14562                             r.select();
14563                         }
14564                     }
14565                 }
14566                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14567                     this.cleanUpPaste.defer(100, this);
14568                     return;
14569                 }
14570                 
14571                 
14572             };
14573         }else if(Roo.isOpera){
14574             return function(e){
14575                 var k = e.getKey();
14576                 if(k == e.TAB){
14577                     e.stopEvent();
14578                     this.win.focus();
14579                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14580                     this.deferFocus();
14581                 }
14582                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14583                     this.cleanUpPaste.defer(100, this);
14584                     return;
14585                 }
14586                 
14587             };
14588         }else if(Roo.isSafari){
14589             return function(e){
14590                 var k = e.getKey();
14591                 
14592                 if(k == e.TAB){
14593                     e.stopEvent();
14594                     this.execCmd('InsertText','\t');
14595                     this.deferFocus();
14596                     return;
14597                 }
14598                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14599                     this.cleanUpPaste.defer(100, this);
14600                     return;
14601                 }
14602                 
14603              };
14604         }
14605     }(),
14606     
14607     getAllAncestors: function()
14608     {
14609         var p = this.getSelectedNode();
14610         var a = [];
14611         if (!p) {
14612             a.push(p); // push blank onto stack..
14613             p = this.getParentElement();
14614         }
14615         
14616         
14617         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14618             a.push(p);
14619             p = p.parentNode;
14620         }
14621         a.push(this.doc.body);
14622         return a;
14623     },
14624     lastSel : false,
14625     lastSelNode : false,
14626     
14627     
14628     getSelection : function() 
14629     {
14630         this.assignDocWin();
14631         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14632     },
14633     
14634     getSelectedNode: function() 
14635     {
14636         // this may only work on Gecko!!!
14637         
14638         // should we cache this!!!!
14639         
14640         
14641         
14642          
14643         var range = this.createRange(this.getSelection()).cloneRange();
14644         
14645         if (Roo.isIE) {
14646             var parent = range.parentElement();
14647             while (true) {
14648                 var testRange = range.duplicate();
14649                 testRange.moveToElementText(parent);
14650                 if (testRange.inRange(range)) {
14651                     break;
14652                 }
14653                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14654                     break;
14655                 }
14656                 parent = parent.parentElement;
14657             }
14658             return parent;
14659         }
14660         
14661         // is ancestor a text element.
14662         var ac =  range.commonAncestorContainer;
14663         if (ac.nodeType == 3) {
14664             ac = ac.parentNode;
14665         }
14666         
14667         var ar = ac.childNodes;
14668          
14669         var nodes = [];
14670         var other_nodes = [];
14671         var has_other_nodes = false;
14672         for (var i=0;i<ar.length;i++) {
14673             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14674                 continue;
14675             }
14676             // fullly contained node.
14677             
14678             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14679                 nodes.push(ar[i]);
14680                 continue;
14681             }
14682             
14683             // probably selected..
14684             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14685                 other_nodes.push(ar[i]);
14686                 continue;
14687             }
14688             // outer..
14689             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14690                 continue;
14691             }
14692             
14693             
14694             has_other_nodes = true;
14695         }
14696         if (!nodes.length && other_nodes.length) {
14697             nodes= other_nodes;
14698         }
14699         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14700             return false;
14701         }
14702         
14703         return nodes[0];
14704     },
14705     createRange: function(sel)
14706     {
14707         // this has strange effects when using with 
14708         // top toolbar - not sure if it's a great idea.
14709         //this.editor.contentWindow.focus();
14710         if (typeof sel != "undefined") {
14711             try {
14712                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14713             } catch(e) {
14714                 return this.doc.createRange();
14715             }
14716         } else {
14717             return this.doc.createRange();
14718         }
14719     },
14720     getParentElement: function()
14721     {
14722         
14723         this.assignDocWin();
14724         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14725         
14726         var range = this.createRange(sel);
14727          
14728         try {
14729             var p = range.commonAncestorContainer;
14730             while (p.nodeType == 3) { // text node
14731                 p = p.parentNode;
14732             }
14733             return p;
14734         } catch (e) {
14735             return null;
14736         }
14737     
14738     },
14739     /***
14740      *
14741      * Range intersection.. the hard stuff...
14742      *  '-1' = before
14743      *  '0' = hits..
14744      *  '1' = after.
14745      *         [ -- selected range --- ]
14746      *   [fail]                        [fail]
14747      *
14748      *    basically..
14749      *      if end is before start or  hits it. fail.
14750      *      if start is after end or hits it fail.
14751      *
14752      *   if either hits (but other is outside. - then it's not 
14753      *   
14754      *    
14755      **/
14756     
14757     
14758     // @see http://www.thismuchiknow.co.uk/?p=64.
14759     rangeIntersectsNode : function(range, node)
14760     {
14761         var nodeRange = node.ownerDocument.createRange();
14762         try {
14763             nodeRange.selectNode(node);
14764         } catch (e) {
14765             nodeRange.selectNodeContents(node);
14766         }
14767     
14768         var rangeStartRange = range.cloneRange();
14769         rangeStartRange.collapse(true);
14770     
14771         var rangeEndRange = range.cloneRange();
14772         rangeEndRange.collapse(false);
14773     
14774         var nodeStartRange = nodeRange.cloneRange();
14775         nodeStartRange.collapse(true);
14776     
14777         var nodeEndRange = nodeRange.cloneRange();
14778         nodeEndRange.collapse(false);
14779     
14780         return rangeStartRange.compareBoundaryPoints(
14781                  Range.START_TO_START, nodeEndRange) == -1 &&
14782                rangeEndRange.compareBoundaryPoints(
14783                  Range.START_TO_START, nodeStartRange) == 1;
14784         
14785          
14786     },
14787     rangeCompareNode : function(range, node)
14788     {
14789         var nodeRange = node.ownerDocument.createRange();
14790         try {
14791             nodeRange.selectNode(node);
14792         } catch (e) {
14793             nodeRange.selectNodeContents(node);
14794         }
14795         
14796         
14797         range.collapse(true);
14798     
14799         nodeRange.collapse(true);
14800      
14801         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14802         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
14803          
14804         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14805         
14806         var nodeIsBefore   =  ss == 1;
14807         var nodeIsAfter    = ee == -1;
14808         
14809         if (nodeIsBefore && nodeIsAfter)
14810             return 0; // outer
14811         if (!nodeIsBefore && nodeIsAfter)
14812             return 1; //right trailed.
14813         
14814         if (nodeIsBefore && !nodeIsAfter)
14815             return 2;  // left trailed.
14816         // fully contined.
14817         return 3;
14818     },
14819
14820     // private? - in a new class?
14821     cleanUpPaste :  function()
14822     {
14823         // cleans up the whole document..
14824         Roo.log('cleanuppaste');
14825         
14826         this.cleanUpChildren(this.doc.body);
14827         var clean = this.cleanWordChars(this.doc.body.innerHTML);
14828         if (clean != this.doc.body.innerHTML) {
14829             this.doc.body.innerHTML = clean;
14830         }
14831         
14832     },
14833     
14834     cleanWordChars : function(input) {// change the chars to hex code
14835         var he = Roo.HtmlEditorCore;
14836         
14837         var output = input;
14838         Roo.each(he.swapCodes, function(sw) { 
14839             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14840             
14841             output = output.replace(swapper, sw[1]);
14842         });
14843         
14844         return output;
14845     },
14846     
14847     
14848     cleanUpChildren : function (n)
14849     {
14850         if (!n.childNodes.length) {
14851             return;
14852         }
14853         for (var i = n.childNodes.length-1; i > -1 ; i--) {
14854            this.cleanUpChild(n.childNodes[i]);
14855         }
14856     },
14857     
14858     
14859         
14860     
14861     cleanUpChild : function (node)
14862     {
14863         var ed = this;
14864         //console.log(node);
14865         if (node.nodeName == "#text") {
14866             // clean up silly Windows -- stuff?
14867             return; 
14868         }
14869         if (node.nodeName == "#comment") {
14870             node.parentNode.removeChild(node);
14871             // clean up silly Windows -- stuff?
14872             return; 
14873         }
14874         
14875         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14876             // remove node.
14877             node.parentNode.removeChild(node);
14878             return;
14879             
14880         }
14881         
14882         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14883         
14884         // remove <a name=....> as rendering on yahoo mailer is borked with this.
14885         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14886         
14887         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14888         //    remove_keep_children = true;
14889         //}
14890         
14891         if (remove_keep_children) {
14892             this.cleanUpChildren(node);
14893             // inserts everything just before this node...
14894             while (node.childNodes.length) {
14895                 var cn = node.childNodes[0];
14896                 node.removeChild(cn);
14897                 node.parentNode.insertBefore(cn, node);
14898             }
14899             node.parentNode.removeChild(node);
14900             return;
14901         }
14902         
14903         if (!node.attributes || !node.attributes.length) {
14904             this.cleanUpChildren(node);
14905             return;
14906         }
14907         
14908         function cleanAttr(n,v)
14909         {
14910             
14911             if (v.match(/^\./) || v.match(/^\//)) {
14912                 return;
14913             }
14914             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14915                 return;
14916             }
14917             if (v.match(/^#/)) {
14918                 return;
14919             }
14920 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14921             node.removeAttribute(n);
14922             
14923         }
14924         
14925         function cleanStyle(n,v)
14926         {
14927             if (v.match(/expression/)) { //XSS?? should we even bother..
14928                 node.removeAttribute(n);
14929                 return;
14930             }
14931             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14932             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14933             
14934             
14935             var parts = v.split(/;/);
14936             var clean = [];
14937             
14938             Roo.each(parts, function(p) {
14939                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14940                 if (!p.length) {
14941                     return true;
14942                 }
14943                 var l = p.split(':').shift().replace(/\s+/g,'');
14944                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14945                 
14946                 if ( cblack.indexOf(l) > -1) {
14947 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14948                     //node.removeAttribute(n);
14949                     return true;
14950                 }
14951                 //Roo.log()
14952                 // only allow 'c whitelisted system attributes'
14953                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
14954 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14955                     //node.removeAttribute(n);
14956                     return true;
14957                 }
14958                 
14959                 
14960                  
14961                 
14962                 clean.push(p);
14963                 return true;
14964             });
14965             if (clean.length) { 
14966                 node.setAttribute(n, clean.join(';'));
14967             } else {
14968                 node.removeAttribute(n);
14969             }
14970             
14971         }
14972         
14973         
14974         for (var i = node.attributes.length-1; i > -1 ; i--) {
14975             var a = node.attributes[i];
14976             //console.log(a);
14977             
14978             if (a.name.toLowerCase().substr(0,2)=='on')  {
14979                 node.removeAttribute(a.name);
14980                 continue;
14981             }
14982             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14983                 node.removeAttribute(a.name);
14984                 continue;
14985             }
14986             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14987                 cleanAttr(a.name,a.value); // fixme..
14988                 continue;
14989             }
14990             if (a.name == 'style') {
14991                 cleanStyle(a.name,a.value);
14992                 continue;
14993             }
14994             /// clean up MS crap..
14995             // tecnically this should be a list of valid class'es..
14996             
14997             
14998             if (a.name == 'class') {
14999                 if (a.value.match(/^Mso/)) {
15000                     node.className = '';
15001                 }
15002                 
15003                 if (a.value.match(/body/)) {
15004                     node.className = '';
15005                 }
15006                 continue;
15007             }
15008             
15009             // style cleanup!?
15010             // class cleanup?
15011             
15012         }
15013         
15014         
15015         this.cleanUpChildren(node);
15016         
15017         
15018     }
15019     
15020     
15021     // hide stuff that is not compatible
15022     /**
15023      * @event blur
15024      * @hide
15025      */
15026     /**
15027      * @event change
15028      * @hide
15029      */
15030     /**
15031      * @event focus
15032      * @hide
15033      */
15034     /**
15035      * @event specialkey
15036      * @hide
15037      */
15038     /**
15039      * @cfg {String} fieldClass @hide
15040      */
15041     /**
15042      * @cfg {String} focusClass @hide
15043      */
15044     /**
15045      * @cfg {String} autoCreate @hide
15046      */
15047     /**
15048      * @cfg {String} inputType @hide
15049      */
15050     /**
15051      * @cfg {String} invalidClass @hide
15052      */
15053     /**
15054      * @cfg {String} invalidText @hide
15055      */
15056     /**
15057      * @cfg {String} msgFx @hide
15058      */
15059     /**
15060      * @cfg {String} validateOnBlur @hide
15061      */
15062 });
15063
15064 Roo.HtmlEditorCore.white = [
15065         'area', 'br', 'img', 'input', 'hr', 'wbr',
15066         
15067        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15068        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15069        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15070        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15071        'table',   'ul',         'xmp', 
15072        
15073        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15074       'thead',   'tr', 
15075      
15076       'dir', 'menu', 'ol', 'ul', 'dl',
15077        
15078       'embed',  'object'
15079 ];
15080
15081
15082 Roo.HtmlEditorCore.black = [
15083     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15084         'applet', // 
15085         'base',   'basefont', 'bgsound', 'blink',  'body', 
15086         'frame',  'frameset', 'head',    'html',   'ilayer', 
15087         'iframe', 'layer',  'link',     'meta',    'object',   
15088         'script', 'style' ,'title',  'xml' // clean later..
15089 ];
15090 Roo.HtmlEditorCore.clean = [
15091     'script', 'style', 'title', 'xml'
15092 ];
15093 Roo.HtmlEditorCore.remove = [
15094     'font'
15095 ];
15096 // attributes..
15097
15098 Roo.HtmlEditorCore.ablack = [
15099     'on'
15100 ];
15101     
15102 Roo.HtmlEditorCore.aclean = [ 
15103     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15104 ];
15105
15106 // protocols..
15107 Roo.HtmlEditorCore.pwhite= [
15108         'http',  'https',  'mailto'
15109 ];
15110
15111 // white listed style attributes.
15112 Roo.HtmlEditorCore.cwhite= [
15113       //  'text-align', /// default is to allow most things..
15114       
15115          
15116 //        'font-size'//??
15117 ];
15118
15119 // black listed style attributes.
15120 Roo.HtmlEditorCore.cblack= [
15121       //  'font-size' -- this can be set by the project 
15122 ];
15123
15124
15125 Roo.HtmlEditorCore.swapCodes   =[ 
15126     [    8211, "--" ], 
15127     [    8212, "--" ], 
15128     [    8216,  "'" ],  
15129     [    8217, "'" ],  
15130     [    8220, '"' ],  
15131     [    8221, '"' ],  
15132     [    8226, "*" ],  
15133     [    8230, "..." ]
15134 ]; 
15135
15136     /*
15137  * - LGPL
15138  *
15139  * HtmlEditor
15140  * 
15141  */
15142
15143 /**
15144  * @class Roo.bootstrap.HtmlEditor
15145  * @extends Roo.bootstrap.TextArea
15146  * Bootstrap HtmlEditor class
15147
15148  * @constructor
15149  * Create a new HtmlEditor
15150  * @param {Object} config The config object
15151  */
15152
15153 Roo.bootstrap.HtmlEditor = function(config){
15154     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15155     if (!this.toolbars) {
15156         this.toolbars = [];
15157     }
15158     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15159     this.addEvents({
15160             /**
15161              * @event initialize
15162              * Fires when the editor is fully initialized (including the iframe)
15163              * @param {HtmlEditor} this
15164              */
15165             initialize: true,
15166             /**
15167              * @event activate
15168              * Fires when the editor is first receives the focus. Any insertion must wait
15169              * until after this event.
15170              * @param {HtmlEditor} this
15171              */
15172             activate: true,
15173              /**
15174              * @event beforesync
15175              * Fires before the textarea is updated with content from the editor iframe. Return false
15176              * to cancel the sync.
15177              * @param {HtmlEditor} this
15178              * @param {String} html
15179              */
15180             beforesync: true,
15181              /**
15182              * @event beforepush
15183              * Fires before the iframe editor is updated with content from the textarea. Return false
15184              * to cancel the push.
15185              * @param {HtmlEditor} this
15186              * @param {String} html
15187              */
15188             beforepush: true,
15189              /**
15190              * @event sync
15191              * Fires when the textarea is updated with content from the editor iframe.
15192              * @param {HtmlEditor} this
15193              * @param {String} html
15194              */
15195             sync: true,
15196              /**
15197              * @event push
15198              * Fires when the iframe editor is updated with content from the textarea.
15199              * @param {HtmlEditor} this
15200              * @param {String} html
15201              */
15202             push: true,
15203              /**
15204              * @event editmodechange
15205              * Fires when the editor switches edit modes
15206              * @param {HtmlEditor} this
15207              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15208              */
15209             editmodechange: true,
15210             /**
15211              * @event editorevent
15212              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15213              * @param {HtmlEditor} this
15214              */
15215             editorevent: true,
15216             /**
15217              * @event firstfocus
15218              * Fires when on first focus - needed by toolbars..
15219              * @param {HtmlEditor} this
15220              */
15221             firstfocus: true,
15222             /**
15223              * @event autosave
15224              * Auto save the htmlEditor value as a file into Events
15225              * @param {HtmlEditor} this
15226              */
15227             autosave: true,
15228             /**
15229              * @event savedpreview
15230              * preview the saved version of htmlEditor
15231              * @param {HtmlEditor} this
15232              */
15233             savedpreview: true
15234         });
15235 };
15236
15237
15238 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15239     
15240     
15241       /**
15242      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15243      */
15244     toolbars : false,
15245    
15246      /**
15247      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15248      *                        Roo.resizable.
15249      */
15250     resizable : false,
15251      /**
15252      * @cfg {Number} height (in pixels)
15253      */   
15254     height: 300,
15255    /**
15256      * @cfg {Number} width (in pixels)
15257      */   
15258     width: false,
15259     
15260     /**
15261      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15262      * 
15263      */
15264     stylesheets: false,
15265     
15266     // id of frame..
15267     frameId: false,
15268     
15269     // private properties
15270     validationEvent : false,
15271     deferHeight: true,
15272     initialized : false,
15273     activated : false,
15274     
15275     onFocus : Roo.emptyFn,
15276     iframePad:3,
15277     hideMode:'offsets',
15278     
15279     
15280     tbContainer : false,
15281     
15282     toolbarContainer :function() {
15283         return this.wrap.select('.x-html-editor-tb',true).first();
15284     },
15285
15286     /**
15287      * Protected method that will not generally be called directly. It
15288      * is called when the editor creates its toolbar. Override this method if you need to
15289      * add custom toolbar buttons.
15290      * @param {HtmlEditor} editor
15291      */
15292     createToolbar : function(){
15293         
15294         Roo.log("create toolbars");
15295         
15296         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15297         this.toolbars[0].render(this.toolbarContainer());
15298         
15299         return;
15300         
15301 //        if (!editor.toolbars || !editor.toolbars.length) {
15302 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15303 //        }
15304 //        
15305 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15306 //            editor.toolbars[i] = Roo.factory(
15307 //                    typeof(editor.toolbars[i]) == 'string' ?
15308 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15309 //                Roo.bootstrap.HtmlEditor);
15310 //            editor.toolbars[i].init(editor);
15311 //        }
15312     },
15313
15314      
15315     // private
15316     onRender : function(ct, position)
15317     {
15318        // Roo.log("Call onRender: " + this.xtype);
15319         var _t = this;
15320         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15321       
15322         this.wrap = this.inputEl().wrap({
15323             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15324         });
15325         
15326         this.editorcore.onRender(ct, position);
15327          
15328         if (this.resizable) {
15329             this.resizeEl = new Roo.Resizable(this.wrap, {
15330                 pinned : true,
15331                 wrap: true,
15332                 dynamic : true,
15333                 minHeight : this.height,
15334                 height: this.height,
15335                 handles : this.resizable,
15336                 width: this.width,
15337                 listeners : {
15338                     resize : function(r, w, h) {
15339                         _t.onResize(w,h); // -something
15340                     }
15341                 }
15342             });
15343             
15344         }
15345         this.createToolbar(this);
15346        
15347         
15348         if(!this.width && this.resizable){
15349             this.setSize(this.wrap.getSize());
15350         }
15351         if (this.resizeEl) {
15352             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15353             // should trigger onReize..
15354         }
15355         
15356     },
15357
15358     // private
15359     onResize : function(w, h)
15360     {
15361         Roo.log('resize: ' +w + ',' + h );
15362         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15363         var ew = false;
15364         var eh = false;
15365         
15366         if(this.inputEl() ){
15367             if(typeof w == 'number'){
15368                 var aw = w - this.wrap.getFrameWidth('lr');
15369                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15370                 ew = aw;
15371             }
15372             if(typeof h == 'number'){
15373                  var tbh = -11;  // fixme it needs to tool bar size!
15374                 for (var i =0; i < this.toolbars.length;i++) {
15375                     // fixme - ask toolbars for heights?
15376                     tbh += this.toolbars[i].el.getHeight();
15377                     //if (this.toolbars[i].footer) {
15378                     //    tbh += this.toolbars[i].footer.el.getHeight();
15379                     //}
15380                 }
15381               
15382                 
15383                 
15384                 
15385                 
15386                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15387                 ah -= 5; // knock a few pixes off for look..
15388                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15389                 var eh = ah;
15390             }
15391         }
15392         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15393         this.editorcore.onResize(ew,eh);
15394         
15395     },
15396
15397     /**
15398      * Toggles the editor between standard and source edit mode.
15399      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15400      */
15401     toggleSourceEdit : function(sourceEditMode)
15402     {
15403         this.editorcore.toggleSourceEdit(sourceEditMode);
15404         
15405         if(this.editorcore.sourceEditMode){
15406             Roo.log('editor - showing textarea');
15407             
15408 //            Roo.log('in');
15409 //            Roo.log(this.syncValue());
15410             this.syncValue();
15411             this.inputEl().removeClass('hide');
15412             this.inputEl().dom.removeAttribute('tabIndex');
15413             this.inputEl().focus();
15414         }else{
15415             Roo.log('editor - hiding textarea');
15416 //            Roo.log('out')
15417 //            Roo.log(this.pushValue()); 
15418             this.pushValue();
15419             
15420             this.inputEl().addClass('hide');
15421             this.inputEl().dom.setAttribute('tabIndex', -1);
15422             //this.deferFocus();
15423         }
15424          
15425         if(this.resizable){
15426             this.setSize(this.wrap.getSize());
15427         }
15428         
15429         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15430     },
15431  
15432     // private (for BoxComponent)
15433     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15434
15435     // private (for BoxComponent)
15436     getResizeEl : function(){
15437         return this.wrap;
15438     },
15439
15440     // private (for BoxComponent)
15441     getPositionEl : function(){
15442         return this.wrap;
15443     },
15444
15445     // private
15446     initEvents : function(){
15447         this.originalValue = this.getValue();
15448     },
15449
15450 //    /**
15451 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15452 //     * @method
15453 //     */
15454 //    markInvalid : Roo.emptyFn,
15455 //    /**
15456 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15457 //     * @method
15458 //     */
15459 //    clearInvalid : Roo.emptyFn,
15460
15461     setValue : function(v){
15462         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15463         this.editorcore.pushValue();
15464     },
15465
15466      
15467     // private
15468     deferFocus : function(){
15469         this.focus.defer(10, this);
15470     },
15471
15472     // doc'ed in Field
15473     focus : function(){
15474         this.editorcore.focus();
15475         
15476     },
15477       
15478
15479     // private
15480     onDestroy : function(){
15481         
15482         
15483         
15484         if(this.rendered){
15485             
15486             for (var i =0; i < this.toolbars.length;i++) {
15487                 // fixme - ask toolbars for heights?
15488                 this.toolbars[i].onDestroy();
15489             }
15490             
15491             this.wrap.dom.innerHTML = '';
15492             this.wrap.remove();
15493         }
15494     },
15495
15496     // private
15497     onFirstFocus : function(){
15498         //Roo.log("onFirstFocus");
15499         this.editorcore.onFirstFocus();
15500          for (var i =0; i < this.toolbars.length;i++) {
15501             this.toolbars[i].onFirstFocus();
15502         }
15503         
15504     },
15505     
15506     // private
15507     syncValue : function()
15508     {   
15509         this.editorcore.syncValue();
15510     },
15511     
15512     pushValue : function()
15513     {   
15514         this.editorcore.pushValue();
15515     }
15516      
15517     
15518     // hide stuff that is not compatible
15519     /**
15520      * @event blur
15521      * @hide
15522      */
15523     /**
15524      * @event change
15525      * @hide
15526      */
15527     /**
15528      * @event focus
15529      * @hide
15530      */
15531     /**
15532      * @event specialkey
15533      * @hide
15534      */
15535     /**
15536      * @cfg {String} fieldClass @hide
15537      */
15538     /**
15539      * @cfg {String} focusClass @hide
15540      */
15541     /**
15542      * @cfg {String} autoCreate @hide
15543      */
15544     /**
15545      * @cfg {String} inputType @hide
15546      */
15547     /**
15548      * @cfg {String} invalidClass @hide
15549      */
15550     /**
15551      * @cfg {String} invalidText @hide
15552      */
15553     /**
15554      * @cfg {String} msgFx @hide
15555      */
15556     /**
15557      * @cfg {String} validateOnBlur @hide
15558      */
15559 });
15560  
15561     
15562    
15563    
15564    
15565       
15566
15567 /**
15568  * @class Roo.bootstrap.HtmlEditorToolbar1
15569  * Basic Toolbar
15570  * 
15571  * Usage:
15572  *
15573  new Roo.bootstrap.HtmlEditor({
15574     ....
15575     toolbars : [
15576         new Roo.bootstrap.HtmlEditorToolbar1({
15577             disable : { fonts: 1 , format: 1, ..., ... , ...],
15578             btns : [ .... ]
15579         })
15580     }
15581      
15582  * 
15583  * @cfg {Object} disable List of elements to disable..
15584  * @cfg {Array} btns List of additional buttons.
15585  * 
15586  * 
15587  * NEEDS Extra CSS? 
15588  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15589  */
15590  
15591 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15592 {
15593     
15594     Roo.apply(this, config);
15595     
15596     // default disabled, based on 'good practice'..
15597     this.disable = this.disable || {};
15598     Roo.applyIf(this.disable, {
15599         fontSize : true,
15600         colors : true,
15601         specialElements : true
15602     });
15603     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15604     
15605     this.editor = config.editor;
15606     this.editorcore = config.editor.editorcore;
15607     
15608     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15609     
15610     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15611     // dont call parent... till later.
15612 }
15613 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
15614     
15615     
15616     bar : true,
15617     
15618     editor : false,
15619     editorcore : false,
15620     
15621     
15622     formats : [
15623         "p" ,  
15624         "h1","h2","h3","h4","h5","h6", 
15625         "pre", "code", 
15626         "abbr", "acronym", "address", "cite", "samp", "var",
15627         'div','span'
15628     ],
15629     
15630     onRender : function(ct, position)
15631     {
15632        // Roo.log("Call onRender: " + this.xtype);
15633         
15634        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15635        Roo.log(this.el);
15636        this.el.dom.style.marginBottom = '0';
15637        var _this = this;
15638        var editorcore = this.editorcore;
15639        var editor= this.editor;
15640        
15641        var children = [];
15642        var btn = function(id,cmd , toggle, handler){
15643        
15644             var  event = toggle ? 'toggle' : 'click';
15645        
15646             var a = {
15647                 size : 'sm',
15648                 xtype: 'Button',
15649                 xns: Roo.bootstrap,
15650                 glyphicon : id,
15651                 cmd : id || cmd,
15652                 enableToggle:toggle !== false,
15653                 //html : 'submit'
15654                 pressed : toggle ? false : null,
15655                 listeners : {}
15656             }
15657             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15658                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15659             }
15660             children.push(a);
15661             return a;
15662        }
15663         
15664         var style = {
15665                 xtype: 'Button',
15666                 size : 'sm',
15667                 xns: Roo.bootstrap,
15668                 glyphicon : 'font',
15669                 //html : 'submit'
15670                 menu : {
15671                     xtype: 'Menu',
15672                     xns: Roo.bootstrap,
15673                     items:  []
15674                 }
15675         };
15676         Roo.each(this.formats, function(f) {
15677             style.menu.items.push({
15678                 xtype :'MenuItem',
15679                 xns: Roo.bootstrap,
15680                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15681                 tagname : f,
15682                 listeners : {
15683                     click : function()
15684                     {
15685                         editorcore.insertTag(this.tagname);
15686                         editor.focus();
15687                     }
15688                 }
15689                 
15690             });
15691         });
15692          children.push(style);   
15693             
15694             
15695         btn('bold',false,true);
15696         btn('italic',false,true);
15697         btn('align-left', 'justifyleft',true);
15698         btn('align-center', 'justifycenter',true);
15699         btn('align-right' , 'justifyright',true);
15700         btn('link', false, false, function(btn) {
15701             //Roo.log("create link?");
15702             var url = prompt(this.createLinkText, this.defaultLinkValue);
15703             if(url && url != 'http:/'+'/'){
15704                 this.editorcore.relayCmd('createlink', url);
15705             }
15706         }),
15707         btn('list','insertunorderedlist',true);
15708         btn('pencil', false,true, function(btn){
15709                 Roo.log(this);
15710                 
15711                 this.toggleSourceEdit(btn.pressed);
15712         });
15713         /*
15714         var cog = {
15715                 xtype: 'Button',
15716                 size : 'sm',
15717                 xns: Roo.bootstrap,
15718                 glyphicon : 'cog',
15719                 //html : 'submit'
15720                 menu : {
15721                     xtype: 'Menu',
15722                     xns: Roo.bootstrap,
15723                     items:  []
15724                 }
15725         };
15726         
15727         cog.menu.items.push({
15728             xtype :'MenuItem',
15729             xns: Roo.bootstrap,
15730             html : Clean styles,
15731             tagname : f,
15732             listeners : {
15733                 click : function()
15734                 {
15735                     editorcore.insertTag(this.tagname);
15736                     editor.focus();
15737                 }
15738             }
15739             
15740         });
15741        */
15742         
15743          
15744        this.xtype = 'NavSimplebar';
15745         
15746         for(var i=0;i< children.length;i++) {
15747             
15748             this.buttons.add(this.addxtypeChild(children[i]));
15749             
15750         }
15751         
15752         editor.on('editorevent', this.updateToolbar, this);
15753     },
15754     onBtnClick : function(id)
15755     {
15756        this.editorcore.relayCmd(id);
15757        this.editorcore.focus();
15758     },
15759     
15760     /**
15761      * Protected method that will not generally be called directly. It triggers
15762      * a toolbar update by reading the markup state of the current selection in the editor.
15763      */
15764     updateToolbar: function(){
15765
15766         if(!this.editorcore.activated){
15767             this.editor.onFirstFocus(); // is this neeed?
15768             return;
15769         }
15770
15771         var btns = this.buttons; 
15772         var doc = this.editorcore.doc;
15773         btns.get('bold').setActive(doc.queryCommandState('bold'));
15774         btns.get('italic').setActive(doc.queryCommandState('italic'));
15775         //btns.get('underline').setActive(doc.queryCommandState('underline'));
15776         
15777         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15778         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15779         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15780         
15781         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15782         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15783          /*
15784         
15785         var ans = this.editorcore.getAllAncestors();
15786         if (this.formatCombo) {
15787             
15788             
15789             var store = this.formatCombo.store;
15790             this.formatCombo.setValue("");
15791             for (var i =0; i < ans.length;i++) {
15792                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15793                     // select it..
15794                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15795                     break;
15796                 }
15797             }
15798         }
15799         
15800         
15801         
15802         // hides menus... - so this cant be on a menu...
15803         Roo.bootstrap.MenuMgr.hideAll();
15804         */
15805         Roo.bootstrap.MenuMgr.hideAll();
15806         //this.editorsyncValue();
15807     },
15808     onFirstFocus: function() {
15809         this.buttons.each(function(item){
15810            item.enable();
15811         });
15812     },
15813     toggleSourceEdit : function(sourceEditMode){
15814         
15815           
15816         if(sourceEditMode){
15817             Roo.log("disabling buttons");
15818            this.buttons.each( function(item){
15819                 if(item.cmd != 'pencil'){
15820                     item.disable();
15821                 }
15822             });
15823           
15824         }else{
15825             Roo.log("enabling buttons");
15826             if(this.editorcore.initialized){
15827                 this.buttons.each( function(item){
15828                     item.enable();
15829                 });
15830             }
15831             
15832         }
15833         Roo.log("calling toggole on editor");
15834         // tell the editor that it's been pressed..
15835         this.editor.toggleSourceEdit(sourceEditMode);
15836        
15837     }
15838 });
15839
15840
15841
15842
15843
15844 /**
15845  * @class Roo.bootstrap.Table.AbstractSelectionModel
15846  * @extends Roo.util.Observable
15847  * Abstract base class for grid SelectionModels.  It provides the interface that should be
15848  * implemented by descendant classes.  This class should not be directly instantiated.
15849  * @constructor
15850  */
15851 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15852     this.locked = false;
15853     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15854 };
15855
15856
15857 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
15858     /** @ignore Called by the grid automatically. Do not call directly. */
15859     init : function(grid){
15860         this.grid = grid;
15861         this.initEvents();
15862     },
15863
15864     /**
15865      * Locks the selections.
15866      */
15867     lock : function(){
15868         this.locked = true;
15869     },
15870
15871     /**
15872      * Unlocks the selections.
15873      */
15874     unlock : function(){
15875         this.locked = false;
15876     },
15877
15878     /**
15879      * Returns true if the selections are locked.
15880      * @return {Boolean}
15881      */
15882     isLocked : function(){
15883         return this.locked;
15884     }
15885 });
15886 /**
15887  * @class Roo.bootstrap.Table.ColumnModel
15888  * @extends Roo.util.Observable
15889  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15890  * the columns in the table.
15891  
15892  * @constructor
15893  * @param {Object} config An Array of column config objects. See this class's
15894  * config objects for details.
15895 */
15896 Roo.bootstrap.Table.ColumnModel = function(config){
15897         /**
15898      * The config passed into the constructor
15899      */
15900     this.config = config;
15901     this.lookup = {};
15902
15903     // if no id, create one
15904     // if the column does not have a dataIndex mapping,
15905     // map it to the order it is in the config
15906     for(var i = 0, len = config.length; i < len; i++){
15907         var c = config[i];
15908         if(typeof c.dataIndex == "undefined"){
15909             c.dataIndex = i;
15910         }
15911         if(typeof c.renderer == "string"){
15912             c.renderer = Roo.util.Format[c.renderer];
15913         }
15914         if(typeof c.id == "undefined"){
15915             c.id = Roo.id();
15916         }
15917 //        if(c.editor && c.editor.xtype){
15918 //            c.editor  = Roo.factory(c.editor, Roo.grid);
15919 //        }
15920 //        if(c.editor && c.editor.isFormField){
15921 //            c.editor = new Roo.grid.GridEditor(c.editor);
15922 //        }
15923
15924         this.lookup[c.id] = c;
15925     }
15926
15927     /**
15928      * The width of columns which have no width specified (defaults to 100)
15929      * @type Number
15930      */
15931     this.defaultWidth = 100;
15932
15933     /**
15934      * Default sortable of columns which have no sortable specified (defaults to false)
15935      * @type Boolean
15936      */
15937     this.defaultSortable = false;
15938
15939     this.addEvents({
15940         /**
15941              * @event widthchange
15942              * Fires when the width of a column changes.
15943              * @param {ColumnModel} this
15944              * @param {Number} columnIndex The column index
15945              * @param {Number} newWidth The new width
15946              */
15947             "widthchange": true,
15948         /**
15949              * @event headerchange
15950              * Fires when the text of a header changes.
15951              * @param {ColumnModel} this
15952              * @param {Number} columnIndex The column index
15953              * @param {Number} newText The new header text
15954              */
15955             "headerchange": true,
15956         /**
15957              * @event hiddenchange
15958              * Fires when a column is hidden or "unhidden".
15959              * @param {ColumnModel} this
15960              * @param {Number} columnIndex The column index
15961              * @param {Boolean} hidden true if hidden, false otherwise
15962              */
15963             "hiddenchange": true,
15964             /**
15965          * @event columnmoved
15966          * Fires when a column is moved.
15967          * @param {ColumnModel} this
15968          * @param {Number} oldIndex
15969          * @param {Number} newIndex
15970          */
15971         "columnmoved" : true,
15972         /**
15973          * @event columlockchange
15974          * Fires when a column's locked state is changed
15975          * @param {ColumnModel} this
15976          * @param {Number} colIndex
15977          * @param {Boolean} locked true if locked
15978          */
15979         "columnlockchange" : true
15980     });
15981     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15982 };
15983 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15984     /**
15985      * @cfg {String} header The header text to display in the Grid view.
15986      */
15987     /**
15988      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15989      * {@link Roo.data.Record} definition from which to draw the column's value. If not
15990      * specified, the column's index is used as an index into the Record's data Array.
15991      */
15992     /**
15993      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15994      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15995      */
15996     /**
15997      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15998      * Defaults to the value of the {@link #defaultSortable} property.
15999      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16000      */
16001     /**
16002      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16003      */
16004     /**
16005      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16006      */
16007     /**
16008      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16009      */
16010     /**
16011      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16012      */
16013     /**
16014      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16015      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16016      * default renderer uses the raw data value.
16017      */
16018     /**
16019      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16020      */
16021
16022     /**
16023      * Returns the id of the column at the specified index.
16024      * @param {Number} index The column index
16025      * @return {String} the id
16026      */
16027     getColumnId : function(index){
16028         return this.config[index].id;
16029     },
16030
16031     /**
16032      * Returns the column for a specified id.
16033      * @param {String} id The column id
16034      * @return {Object} the column
16035      */
16036     getColumnById : function(id){
16037         return this.lookup[id];
16038     },
16039
16040     
16041     /**
16042      * Returns the column for a specified dataIndex.
16043      * @param {String} dataIndex The column dataIndex
16044      * @return {Object|Boolean} the column or false if not found
16045      */
16046     getColumnByDataIndex: function(dataIndex){
16047         var index = this.findColumnIndex(dataIndex);
16048         return index > -1 ? this.config[index] : false;
16049     },
16050     
16051     /**
16052      * Returns the index for a specified column id.
16053      * @param {String} id The column id
16054      * @return {Number} the index, or -1 if not found
16055      */
16056     getIndexById : function(id){
16057         for(var i = 0, len = this.config.length; i < len; i++){
16058             if(this.config[i].id == id){
16059                 return i;
16060             }
16061         }
16062         return -1;
16063     },
16064     
16065     /**
16066      * Returns the index for a specified column dataIndex.
16067      * @param {String} dataIndex The column dataIndex
16068      * @return {Number} the index, or -1 if not found
16069      */
16070     
16071     findColumnIndex : function(dataIndex){
16072         for(var i = 0, len = this.config.length; i < len; i++){
16073             if(this.config[i].dataIndex == dataIndex){
16074                 return i;
16075             }
16076         }
16077         return -1;
16078     },
16079     
16080     
16081     moveColumn : function(oldIndex, newIndex){
16082         var c = this.config[oldIndex];
16083         this.config.splice(oldIndex, 1);
16084         this.config.splice(newIndex, 0, c);
16085         this.dataMap = null;
16086         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16087     },
16088
16089     isLocked : function(colIndex){
16090         return this.config[colIndex].locked === true;
16091     },
16092
16093     setLocked : function(colIndex, value, suppressEvent){
16094         if(this.isLocked(colIndex) == value){
16095             return;
16096         }
16097         this.config[colIndex].locked = value;
16098         if(!suppressEvent){
16099             this.fireEvent("columnlockchange", this, colIndex, value);
16100         }
16101     },
16102
16103     getTotalLockedWidth : function(){
16104         var totalWidth = 0;
16105         for(var i = 0; i < this.config.length; i++){
16106             if(this.isLocked(i) && !this.isHidden(i)){
16107                 this.totalWidth += this.getColumnWidth(i);
16108             }
16109         }
16110         return totalWidth;
16111     },
16112
16113     getLockedCount : function(){
16114         for(var i = 0, len = this.config.length; i < len; i++){
16115             if(!this.isLocked(i)){
16116                 return i;
16117             }
16118         }
16119     },
16120
16121     /**
16122      * Returns the number of columns.
16123      * @return {Number}
16124      */
16125     getColumnCount : function(visibleOnly){
16126         if(visibleOnly === true){
16127             var c = 0;
16128             for(var i = 0, len = this.config.length; i < len; i++){
16129                 if(!this.isHidden(i)){
16130                     c++;
16131                 }
16132             }
16133             return c;
16134         }
16135         return this.config.length;
16136     },
16137
16138     /**
16139      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16140      * @param {Function} fn
16141      * @param {Object} scope (optional)
16142      * @return {Array} result
16143      */
16144     getColumnsBy : function(fn, scope){
16145         var r = [];
16146         for(var i = 0, len = this.config.length; i < len; i++){
16147             var c = this.config[i];
16148             if(fn.call(scope||this, c, i) === true){
16149                 r[r.length] = c;
16150             }
16151         }
16152         return r;
16153     },
16154
16155     /**
16156      * Returns true if the specified column is sortable.
16157      * @param {Number} col The column index
16158      * @return {Boolean}
16159      */
16160     isSortable : function(col){
16161         if(typeof this.config[col].sortable == "undefined"){
16162             return this.defaultSortable;
16163         }
16164         return this.config[col].sortable;
16165     },
16166
16167     /**
16168      * Returns the rendering (formatting) function defined for the column.
16169      * @param {Number} col The column index.
16170      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16171      */
16172     getRenderer : function(col){
16173         if(!this.config[col].renderer){
16174             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16175         }
16176         return this.config[col].renderer;
16177     },
16178
16179     /**
16180      * Sets the rendering (formatting) function for a column.
16181      * @param {Number} col The column index
16182      * @param {Function} fn The function to use to process the cell's raw data
16183      * to return HTML markup for the grid view. The render function is called with
16184      * the following parameters:<ul>
16185      * <li>Data value.</li>
16186      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16187      * <li>css A CSS style string to apply to the table cell.</li>
16188      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16189      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16190      * <li>Row index</li>
16191      * <li>Column index</li>
16192      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16193      */
16194     setRenderer : function(col, fn){
16195         this.config[col].renderer = fn;
16196     },
16197
16198     /**
16199      * Returns the width for the specified column.
16200      * @param {Number} col The column index
16201      * @return {Number}
16202      */
16203     getColumnWidth : function(col){
16204         return this.config[col].width * 1 || this.defaultWidth;
16205     },
16206
16207     /**
16208      * Sets the width for a column.
16209      * @param {Number} col The column index
16210      * @param {Number} width The new width
16211      */
16212     setColumnWidth : function(col, width, suppressEvent){
16213         this.config[col].width = width;
16214         this.totalWidth = null;
16215         if(!suppressEvent){
16216              this.fireEvent("widthchange", this, col, width);
16217         }
16218     },
16219
16220     /**
16221      * Returns the total width of all columns.
16222      * @param {Boolean} includeHidden True to include hidden column widths
16223      * @return {Number}
16224      */
16225     getTotalWidth : function(includeHidden){
16226         if(!this.totalWidth){
16227             this.totalWidth = 0;
16228             for(var i = 0, len = this.config.length; i < len; i++){
16229                 if(includeHidden || !this.isHidden(i)){
16230                     this.totalWidth += this.getColumnWidth(i);
16231                 }
16232             }
16233         }
16234         return this.totalWidth;
16235     },
16236
16237     /**
16238      * Returns the header for the specified column.
16239      * @param {Number} col The column index
16240      * @return {String}
16241      */
16242     getColumnHeader : function(col){
16243         return this.config[col].header;
16244     },
16245
16246     /**
16247      * Sets the header for a column.
16248      * @param {Number} col The column index
16249      * @param {String} header The new header
16250      */
16251     setColumnHeader : function(col, header){
16252         this.config[col].header = header;
16253         this.fireEvent("headerchange", this, col, header);
16254     },
16255
16256     /**
16257      * Returns the tooltip for the specified column.
16258      * @param {Number} col The column index
16259      * @return {String}
16260      */
16261     getColumnTooltip : function(col){
16262             return this.config[col].tooltip;
16263     },
16264     /**
16265      * Sets the tooltip for a column.
16266      * @param {Number} col The column index
16267      * @param {String} tooltip The new tooltip
16268      */
16269     setColumnTooltip : function(col, tooltip){
16270             this.config[col].tooltip = tooltip;
16271     },
16272
16273     /**
16274      * Returns the dataIndex for the specified column.
16275      * @param {Number} col The column index
16276      * @return {Number}
16277      */
16278     getDataIndex : function(col){
16279         return this.config[col].dataIndex;
16280     },
16281
16282     /**
16283      * Sets the dataIndex for a column.
16284      * @param {Number} col The column index
16285      * @param {Number} dataIndex The new dataIndex
16286      */
16287     setDataIndex : function(col, dataIndex){
16288         this.config[col].dataIndex = dataIndex;
16289     },
16290
16291     
16292     
16293     /**
16294      * Returns true if the cell is editable.
16295      * @param {Number} colIndex The column index
16296      * @param {Number} rowIndex The row index
16297      * @return {Boolean}
16298      */
16299     isCellEditable : function(colIndex, rowIndex){
16300         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16301     },
16302
16303     /**
16304      * Returns the editor defined for the cell/column.
16305      * return false or null to disable editing.
16306      * @param {Number} colIndex The column index
16307      * @param {Number} rowIndex The row index
16308      * @return {Object}
16309      */
16310     getCellEditor : function(colIndex, rowIndex){
16311         return this.config[colIndex].editor;
16312     },
16313
16314     /**
16315      * Sets if a column is editable.
16316      * @param {Number} col The column index
16317      * @param {Boolean} editable True if the column is editable
16318      */
16319     setEditable : function(col, editable){
16320         this.config[col].editable = editable;
16321     },
16322
16323
16324     /**
16325      * Returns true if the column is hidden.
16326      * @param {Number} colIndex The column index
16327      * @return {Boolean}
16328      */
16329     isHidden : function(colIndex){
16330         return this.config[colIndex].hidden;
16331     },
16332
16333
16334     /**
16335      * Returns true if the column width cannot be changed
16336      */
16337     isFixed : function(colIndex){
16338         return this.config[colIndex].fixed;
16339     },
16340
16341     /**
16342      * Returns true if the column can be resized
16343      * @return {Boolean}
16344      */
16345     isResizable : function(colIndex){
16346         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16347     },
16348     /**
16349      * Sets if a column is hidden.
16350      * @param {Number} colIndex The column index
16351      * @param {Boolean} hidden True if the column is hidden
16352      */
16353     setHidden : function(colIndex, hidden){
16354         this.config[colIndex].hidden = hidden;
16355         this.totalWidth = null;
16356         this.fireEvent("hiddenchange", this, colIndex, hidden);
16357     },
16358
16359     /**
16360      * Sets the editor for a column.
16361      * @param {Number} col The column index
16362      * @param {Object} editor The editor object
16363      */
16364     setEditor : function(col, editor){
16365         this.config[col].editor = editor;
16366     }
16367 });
16368
16369 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16370         if(typeof value == "string" && value.length < 1){
16371             return "&#160;";
16372         }
16373         return value;
16374 };
16375
16376 // Alias for backwards compatibility
16377 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16378
16379 /**
16380  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16381  * @class Roo.bootstrap.Table.RowSelectionModel
16382  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16383  * It supports multiple selections and keyboard selection/navigation. 
16384  * @constructor
16385  * @param {Object} config
16386  */
16387
16388 Roo.bootstrap.Table.RowSelectionModel = function(config){
16389     Roo.apply(this, config);
16390     this.selections = new Roo.util.MixedCollection(false, function(o){
16391         return o.id;
16392     });
16393
16394     this.last = false;
16395     this.lastActive = false;
16396
16397     this.addEvents({
16398         /**
16399              * @event selectionchange
16400              * Fires when the selection changes
16401              * @param {SelectionModel} this
16402              */
16403             "selectionchange" : true,
16404         /**
16405              * @event afterselectionchange
16406              * Fires after the selection changes (eg. by key press or clicking)
16407              * @param {SelectionModel} this
16408              */
16409             "afterselectionchange" : true,
16410         /**
16411              * @event beforerowselect
16412              * Fires when a row is selected being selected, return false to cancel.
16413              * @param {SelectionModel} this
16414              * @param {Number} rowIndex The selected index
16415              * @param {Boolean} keepExisting False if other selections will be cleared
16416              */
16417             "beforerowselect" : true,
16418         /**
16419              * @event rowselect
16420              * Fires when a row is selected.
16421              * @param {SelectionModel} this
16422              * @param {Number} rowIndex The selected index
16423              * @param {Roo.data.Record} r The record
16424              */
16425             "rowselect" : true,
16426         /**
16427              * @event rowdeselect
16428              * Fires when a row is deselected.
16429              * @param {SelectionModel} this
16430              * @param {Number} rowIndex The selected index
16431              */
16432         "rowdeselect" : true
16433     });
16434     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16435     this.locked = false;
16436 };
16437
16438 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16439     /**
16440      * @cfg {Boolean} singleSelect
16441      * True to allow selection of only one row at a time (defaults to false)
16442      */
16443     singleSelect : false,
16444
16445     // private
16446     initEvents : function(){
16447
16448         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16449             this.grid.on("mousedown", this.handleMouseDown, this);
16450         }else{ // allow click to work like normal
16451             this.grid.on("rowclick", this.handleDragableRowClick, this);
16452         }
16453
16454         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16455             "up" : function(e){
16456                 if(!e.shiftKey){
16457                     this.selectPrevious(e.shiftKey);
16458                 }else if(this.last !== false && this.lastActive !== false){
16459                     var last = this.last;
16460                     this.selectRange(this.last,  this.lastActive-1);
16461                     this.grid.getView().focusRow(this.lastActive);
16462                     if(last !== false){
16463                         this.last = last;
16464                     }
16465                 }else{
16466                     this.selectFirstRow();
16467                 }
16468                 this.fireEvent("afterselectionchange", this);
16469             },
16470             "down" : function(e){
16471                 if(!e.shiftKey){
16472                     this.selectNext(e.shiftKey);
16473                 }else if(this.last !== false && this.lastActive !== false){
16474                     var last = this.last;
16475                     this.selectRange(this.last,  this.lastActive+1);
16476                     this.grid.getView().focusRow(this.lastActive);
16477                     if(last !== false){
16478                         this.last = last;
16479                     }
16480                 }else{
16481                     this.selectFirstRow();
16482                 }
16483                 this.fireEvent("afterselectionchange", this);
16484             },
16485             scope: this
16486         });
16487
16488         var view = this.grid.view;
16489         view.on("refresh", this.onRefresh, this);
16490         view.on("rowupdated", this.onRowUpdated, this);
16491         view.on("rowremoved", this.onRemove, this);
16492     },
16493
16494     // private
16495     onRefresh : function(){
16496         var ds = this.grid.dataSource, i, v = this.grid.view;
16497         var s = this.selections;
16498         s.each(function(r){
16499             if((i = ds.indexOfId(r.id)) != -1){
16500                 v.onRowSelect(i);
16501             }else{
16502                 s.remove(r);
16503             }
16504         });
16505     },
16506
16507     // private
16508     onRemove : function(v, index, r){
16509         this.selections.remove(r);
16510     },
16511
16512     // private
16513     onRowUpdated : function(v, index, r){
16514         if(this.isSelected(r)){
16515             v.onRowSelect(index);
16516         }
16517     },
16518
16519     /**
16520      * Select records.
16521      * @param {Array} records The records to select
16522      * @param {Boolean} keepExisting (optional) True to keep existing selections
16523      */
16524     selectRecords : function(records, keepExisting){
16525         if(!keepExisting){
16526             this.clearSelections();
16527         }
16528         var ds = this.grid.dataSource;
16529         for(var i = 0, len = records.length; i < len; i++){
16530             this.selectRow(ds.indexOf(records[i]), true);
16531         }
16532     },
16533
16534     /**
16535      * Gets the number of selected rows.
16536      * @return {Number}
16537      */
16538     getCount : function(){
16539         return this.selections.length;
16540     },
16541
16542     /**
16543      * Selects the first row in the grid.
16544      */
16545     selectFirstRow : function(){
16546         this.selectRow(0);
16547     },
16548
16549     /**
16550      * Select the last row.
16551      * @param {Boolean} keepExisting (optional) True to keep existing selections
16552      */
16553     selectLastRow : function(keepExisting){
16554         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16555     },
16556
16557     /**
16558      * Selects the row immediately following the last selected row.
16559      * @param {Boolean} keepExisting (optional) True to keep existing selections
16560      */
16561     selectNext : function(keepExisting){
16562         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16563             this.selectRow(this.last+1, keepExisting);
16564             this.grid.getView().focusRow(this.last);
16565         }
16566     },
16567
16568     /**
16569      * Selects the row that precedes the last selected row.
16570      * @param {Boolean} keepExisting (optional) True to keep existing selections
16571      */
16572     selectPrevious : function(keepExisting){
16573         if(this.last){
16574             this.selectRow(this.last-1, keepExisting);
16575             this.grid.getView().focusRow(this.last);
16576         }
16577     },
16578
16579     /**
16580      * Returns the selected records
16581      * @return {Array} Array of selected records
16582      */
16583     getSelections : function(){
16584         return [].concat(this.selections.items);
16585     },
16586
16587     /**
16588      * Returns the first selected record.
16589      * @return {Record}
16590      */
16591     getSelected : function(){
16592         return this.selections.itemAt(0);
16593     },
16594
16595
16596     /**
16597      * Clears all selections.
16598      */
16599     clearSelections : function(fast){
16600         if(this.locked) return;
16601         if(fast !== true){
16602             var ds = this.grid.dataSource;
16603             var s = this.selections;
16604             s.each(function(r){
16605                 this.deselectRow(ds.indexOfId(r.id));
16606             }, this);
16607             s.clear();
16608         }else{
16609             this.selections.clear();
16610         }
16611         this.last = false;
16612     },
16613
16614
16615     /**
16616      * Selects all rows.
16617      */
16618     selectAll : function(){
16619         if(this.locked) return;
16620         this.selections.clear();
16621         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16622             this.selectRow(i, true);
16623         }
16624     },
16625
16626     /**
16627      * Returns True if there is a selection.
16628      * @return {Boolean}
16629      */
16630     hasSelection : function(){
16631         return this.selections.length > 0;
16632     },
16633
16634     /**
16635      * Returns True if the specified row is selected.
16636      * @param {Number/Record} record The record or index of the record to check
16637      * @return {Boolean}
16638      */
16639     isSelected : function(index){
16640         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16641         return (r && this.selections.key(r.id) ? true : false);
16642     },
16643
16644     /**
16645      * Returns True if the specified record id is selected.
16646      * @param {String} id The id of record to check
16647      * @return {Boolean}
16648      */
16649     isIdSelected : function(id){
16650         return (this.selections.key(id) ? true : false);
16651     },
16652
16653     // private
16654     handleMouseDown : function(e, t){
16655         var view = this.grid.getView(), rowIndex;
16656         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16657             return;
16658         };
16659         if(e.shiftKey && this.last !== false){
16660             var last = this.last;
16661             this.selectRange(last, rowIndex, e.ctrlKey);
16662             this.last = last; // reset the last
16663             view.focusRow(rowIndex);
16664         }else{
16665             var isSelected = this.isSelected(rowIndex);
16666             if(e.button !== 0 && isSelected){
16667                 view.focusRow(rowIndex);
16668             }else if(e.ctrlKey && isSelected){
16669                 this.deselectRow(rowIndex);
16670             }else if(!isSelected){
16671                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16672                 view.focusRow(rowIndex);
16673             }
16674         }
16675         this.fireEvent("afterselectionchange", this);
16676     },
16677     // private
16678     handleDragableRowClick :  function(grid, rowIndex, e) 
16679     {
16680         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16681             this.selectRow(rowIndex, false);
16682             grid.view.focusRow(rowIndex);
16683              this.fireEvent("afterselectionchange", this);
16684         }
16685     },
16686     
16687     /**
16688      * Selects multiple rows.
16689      * @param {Array} rows Array of the indexes of the row to select
16690      * @param {Boolean} keepExisting (optional) True to keep existing selections
16691      */
16692     selectRows : function(rows, keepExisting){
16693         if(!keepExisting){
16694             this.clearSelections();
16695         }
16696         for(var i = 0, len = rows.length; i < len; i++){
16697             this.selectRow(rows[i], true);
16698         }
16699     },
16700
16701     /**
16702      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16703      * @param {Number} startRow The index of the first row in the range
16704      * @param {Number} endRow The index of the last row in the range
16705      * @param {Boolean} keepExisting (optional) True to retain existing selections
16706      */
16707     selectRange : function(startRow, endRow, keepExisting){
16708         if(this.locked) return;
16709         if(!keepExisting){
16710             this.clearSelections();
16711         }
16712         if(startRow <= endRow){
16713             for(var i = startRow; i <= endRow; i++){
16714                 this.selectRow(i, true);
16715             }
16716         }else{
16717             for(var i = startRow; i >= endRow; i--){
16718                 this.selectRow(i, true);
16719             }
16720         }
16721     },
16722
16723     /**
16724      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16725      * @param {Number} startRow The index of the first row in the range
16726      * @param {Number} endRow The index of the last row in the range
16727      */
16728     deselectRange : function(startRow, endRow, preventViewNotify){
16729         if(this.locked) return;
16730         for(var i = startRow; i <= endRow; i++){
16731             this.deselectRow(i, preventViewNotify);
16732         }
16733     },
16734
16735     /**
16736      * Selects a row.
16737      * @param {Number} row The index of the row to select
16738      * @param {Boolean} keepExisting (optional) True to keep existing selections
16739      */
16740     selectRow : function(index, keepExisting, preventViewNotify){
16741         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16742         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16743             if(!keepExisting || this.singleSelect){
16744                 this.clearSelections();
16745             }
16746             var r = this.grid.dataSource.getAt(index);
16747             this.selections.add(r);
16748             this.last = this.lastActive = index;
16749             if(!preventViewNotify){
16750                 this.grid.getView().onRowSelect(index);
16751             }
16752             this.fireEvent("rowselect", this, index, r);
16753             this.fireEvent("selectionchange", this);
16754         }
16755     },
16756
16757     /**
16758      * Deselects a row.
16759      * @param {Number} row The index of the row to deselect
16760      */
16761     deselectRow : function(index, preventViewNotify){
16762         if(this.locked) return;
16763         if(this.last == index){
16764             this.last = false;
16765         }
16766         if(this.lastActive == index){
16767             this.lastActive = false;
16768         }
16769         var r = this.grid.dataSource.getAt(index);
16770         this.selections.remove(r);
16771         if(!preventViewNotify){
16772             this.grid.getView().onRowDeselect(index);
16773         }
16774         this.fireEvent("rowdeselect", this, index);
16775         this.fireEvent("selectionchange", this);
16776     },
16777
16778     // private
16779     restoreLast : function(){
16780         if(this._last){
16781             this.last = this._last;
16782         }
16783     },
16784
16785     // private
16786     acceptsNav : function(row, col, cm){
16787         return !cm.isHidden(col) && cm.isCellEditable(col, row);
16788     },
16789
16790     // private
16791     onEditorKey : function(field, e){
16792         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16793         if(k == e.TAB){
16794             e.stopEvent();
16795             ed.completeEdit();
16796             if(e.shiftKey){
16797                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16798             }else{
16799                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16800             }
16801         }else if(k == e.ENTER && !e.ctrlKey){
16802             e.stopEvent();
16803             ed.completeEdit();
16804             if(e.shiftKey){
16805                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16806             }else{
16807                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16808             }
16809         }else if(k == e.ESC){
16810             ed.cancelEdit();
16811         }
16812         if(newCell){
16813             g.startEditing(newCell[0], newCell[1]);
16814         }
16815     }
16816 });/*
16817  * - LGPL
16818  *
16819  * element
16820  * 
16821  */
16822
16823 /**
16824  * @class Roo.bootstrap.MessageBar
16825  * @extends Roo.bootstrap.Component
16826  * Bootstrap MessageBar class
16827  * @cfg {String} html contents of the MessageBar
16828  * @cfg {String} weight (info | success | warning | danger) default info
16829  * @cfg {String} beforeClass insert the bar before the given class
16830  * @cfg {Boolean} closable (true | false) default false
16831  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16832  * 
16833  * @constructor
16834  * Create a new Element
16835  * @param {Object} config The config object
16836  */
16837
16838 Roo.bootstrap.MessageBar = function(config){
16839     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16840 };
16841
16842 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
16843     
16844     html: '',
16845     weight: 'info',
16846     closable: false,
16847     fixed: false,
16848     beforeClass: 'bootstrap-sticky-wrap',
16849     
16850     getAutoCreate : function(){
16851         
16852         var cfg = {
16853             tag: 'div',
16854             cls: 'alert alert-dismissable alert-' + this.weight,
16855             cn: [
16856                 {
16857                     tag: 'span',
16858                     cls: 'message',
16859                     html: this.html || ''
16860                 }
16861             ]
16862         }
16863         
16864         if(this.fixed){
16865             cfg.cls += ' alert-messages-fixed';
16866         }
16867         
16868         if(this.closable){
16869             cfg.cn.push({
16870                 tag: 'button',
16871                 cls: 'close',
16872                 html: 'x'
16873             });
16874         }
16875         
16876         return cfg;
16877     },
16878     
16879     onRender : function(ct, position)
16880     {
16881         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16882         
16883         if(!this.el){
16884             var cfg = Roo.apply({},  this.getAutoCreate());
16885             cfg.id = Roo.id();
16886             
16887             if (this.cls) {
16888                 cfg.cls += ' ' + this.cls;
16889             }
16890             if (this.style) {
16891                 cfg.style = this.style;
16892             }
16893             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16894             
16895             this.el.setVisibilityMode(Roo.Element.DISPLAY);
16896         }
16897         
16898         this.el.select('>button.close').on('click', this.hide, this);
16899         
16900     },
16901     
16902     show : function()
16903     {
16904         if (!this.rendered) {
16905             this.render();
16906         }
16907         
16908         this.el.show();
16909         
16910         this.fireEvent('show', this);
16911         
16912     },
16913     
16914     hide : function()
16915     {
16916         if (!this.rendered) {
16917             this.render();
16918         }
16919         
16920         this.el.hide();
16921         
16922         this.fireEvent('hide', this);
16923     },
16924     
16925     update : function()
16926     {
16927 //        var e = this.el.dom.firstChild;
16928 //        
16929 //        if(this.closable){
16930 //            e = e.nextSibling;
16931 //        }
16932 //        
16933 //        e.data = this.html || '';
16934
16935         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
16936     }
16937    
16938 });
16939
16940  
16941
16942