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         if (body) {
937             body.html = this.html || cfg.html;
938         }
939         if (!this.cls || !this.cls.length) {
940             cfg.cls =  'container';
941         }
942         
943         return cfg;
944     }
945    
946 });
947
948  /*
949  * - LGPL
950  *
951  * image
952  * 
953  */
954
955
956 /**
957  * @class Roo.bootstrap.Img
958  * @extends Roo.bootstrap.Component
959  * Bootstrap Img class
960  * @cfg {Boolean} imgResponsive false | true
961  * @cfg {String} border rounded | circle | thumbnail
962  * @cfg {String} src image source
963  * @cfg {String} alt image alternative text
964  * @cfg {String} href a tag href
965  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
966  * 
967  * @constructor
968  * Create a new Input
969  * @param {Object} config The config object
970  */
971
972 Roo.bootstrap.Img = function(config){
973     Roo.bootstrap.Img.superclass.constructor.call(this, config);
974     
975     this.addEvents({
976         // img events
977         /**
978          * @event click
979          * The img click event for the img.
980          * @param {Roo.EventObject} e
981          */
982         "click" : true
983     });
984 };
985
986 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
987     
988     imgResponsive: true,
989     border: '',
990     src: '',
991     href: false,
992     target: false,
993
994     getAutoCreate : function(){
995         
996         var cfg = {
997             tag: 'img',
998             cls: (this.imgResponsive) ? 'img-responsive' : '',
999             html : null
1000         }
1001         
1002         cfg.html = this.html || cfg.html;
1003         
1004         cfg.src = this.src || cfg.src;
1005         
1006         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1007             cfg.cls += ' img-' + this.border;
1008         }
1009         
1010         if(this.alt){
1011             cfg.alt = this.alt;
1012         }
1013         
1014         if(this.href){
1015             var a = {
1016                 tag: 'a',
1017                 href: this.href,
1018                 cn: [
1019                     cfg
1020                 ]
1021             }
1022             
1023             if(this.target){
1024                 a.target = this.target;
1025             }
1026             
1027         }
1028         
1029         
1030         return (this.href) ? a : cfg;
1031     },
1032     
1033     initEvents: function() {
1034         
1035         if(!this.href){
1036             this.el.on('click', this.onClick, this);
1037         }
1038     },
1039     
1040     onClick : function(e)
1041     {
1042         Roo.log('img onclick');
1043         this.fireEvent('click', this, e);
1044     }
1045    
1046 });
1047
1048  /*
1049  * - LGPL
1050  *
1051  * header
1052  * 
1053  */
1054
1055 /**
1056  * @class Roo.bootstrap.Header
1057  * @extends Roo.bootstrap.Component
1058  * Bootstrap Header class
1059  * @cfg {String} html content of header
1060  * @cfg {Number} level (1|2|3|4|5|6) default 1
1061  * 
1062  * @constructor
1063  * Create a new Header
1064  * @param {Object} config The config object
1065  */
1066
1067
1068 Roo.bootstrap.Header  = function(config){
1069     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1070 };
1071
1072 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1073     
1074     //href : false,
1075     html : false,
1076     level : 1,
1077     
1078     
1079     
1080     getAutoCreate : function(){
1081         
1082         var cfg = {
1083             tag: 'h' + (1 *this.level),
1084             html: this.html || 'fill in html'
1085         } ;
1086         
1087         return cfg;
1088     }
1089    
1090 });
1091
1092  
1093
1094  /*
1095  * Based on:
1096  * Ext JS Library 1.1.1
1097  * Copyright(c) 2006-2007, Ext JS, LLC.
1098  *
1099  * Originally Released Under LGPL - original licence link has changed is not relivant.
1100  *
1101  * Fork - LGPL
1102  * <script type="text/javascript">
1103  */
1104  
1105 /**
1106  * @class Roo.bootstrap.MenuMgr
1107  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1108  * @singleton
1109  */
1110 Roo.bootstrap.MenuMgr = function(){
1111    var menus, active, groups = {}, attached = false, lastShow = new Date();
1112
1113    // private - called when first menu is created
1114    function init(){
1115        menus = {};
1116        active = new Roo.util.MixedCollection();
1117        Roo.get(document).addKeyListener(27, function(){
1118            if(active.length > 0){
1119                hideAll();
1120            }
1121        });
1122    }
1123
1124    // private
1125    function hideAll(){
1126        if(active && active.length > 0){
1127            var c = active.clone();
1128            c.each(function(m){
1129                m.hide();
1130            });
1131        }
1132    }
1133
1134    // private
1135    function onHide(m){
1136        active.remove(m);
1137        if(active.length < 1){
1138            Roo.get(document).un("mouseup", onMouseDown);
1139             
1140            attached = false;
1141        }
1142    }
1143
1144    // private
1145    function onShow(m){
1146        var last = active.last();
1147        lastShow = new Date();
1148        active.add(m);
1149        if(!attached){
1150           Roo.get(document).on("mouseup", onMouseDown);
1151            
1152            attached = true;
1153        }
1154        if(m.parentMenu){
1155           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1156           m.parentMenu.activeChild = m;
1157        }else if(last && last.isVisible()){
1158           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1159        }
1160    }
1161
1162    // private
1163    function onBeforeHide(m){
1164        if(m.activeChild){
1165            m.activeChild.hide();
1166        }
1167        if(m.autoHideTimer){
1168            clearTimeout(m.autoHideTimer);
1169            delete m.autoHideTimer;
1170        }
1171    }
1172
1173    // private
1174    function onBeforeShow(m){
1175        var pm = m.parentMenu;
1176        if(!pm && !m.allowOtherMenus){
1177            hideAll();
1178        }else if(pm && pm.activeChild && active != m){
1179            pm.activeChild.hide();
1180        }
1181    }
1182
1183    // private
1184    function onMouseDown(e){
1185         Roo.log("on MouseDown");
1186         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1187            hideAll();
1188         }
1189         
1190         
1191    }
1192
1193    // private
1194    function onBeforeCheck(mi, state){
1195        if(state){
1196            var g = groups[mi.group];
1197            for(var i = 0, l = g.length; i < l; i++){
1198                if(g[i] != mi){
1199                    g[i].setChecked(false);
1200                }
1201            }
1202        }
1203    }
1204
1205    return {
1206
1207        /**
1208         * Hides all menus that are currently visible
1209         */
1210        hideAll : function(){
1211             hideAll();  
1212        },
1213
1214        // private
1215        register : function(menu){
1216            if(!menus){
1217                init();
1218            }
1219            menus[menu.id] = menu;
1220            menu.on("beforehide", onBeforeHide);
1221            menu.on("hide", onHide);
1222            menu.on("beforeshow", onBeforeShow);
1223            menu.on("show", onShow);
1224            var g = menu.group;
1225            if(g && menu.events["checkchange"]){
1226                if(!groups[g]){
1227                    groups[g] = [];
1228                }
1229                groups[g].push(menu);
1230                menu.on("checkchange", onCheck);
1231            }
1232        },
1233
1234         /**
1235          * Returns a {@link Roo.menu.Menu} object
1236          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1237          * be used to generate and return a new Menu instance.
1238          */
1239        get : function(menu){
1240            if(typeof menu == "string"){ // menu id
1241                return menus[menu];
1242            }else if(menu.events){  // menu instance
1243                return menu;
1244            }
1245            /*else if(typeof menu.length == 'number'){ // array of menu items?
1246                return new Roo.bootstrap.Menu({items:menu});
1247            }else{ // otherwise, must be a config
1248                return new Roo.bootstrap.Menu(menu);
1249            }
1250            */
1251            return false;
1252        },
1253
1254        // private
1255        unregister : function(menu){
1256            delete menus[menu.id];
1257            menu.un("beforehide", onBeforeHide);
1258            menu.un("hide", onHide);
1259            menu.un("beforeshow", onBeforeShow);
1260            menu.un("show", onShow);
1261            var g = menu.group;
1262            if(g && menu.events["checkchange"]){
1263                groups[g].remove(menu);
1264                menu.un("checkchange", onCheck);
1265            }
1266        },
1267
1268        // private
1269        registerCheckable : function(menuItem){
1270            var g = menuItem.group;
1271            if(g){
1272                if(!groups[g]){
1273                    groups[g] = [];
1274                }
1275                groups[g].push(menuItem);
1276                menuItem.on("beforecheckchange", onBeforeCheck);
1277            }
1278        },
1279
1280        // private
1281        unregisterCheckable : function(menuItem){
1282            var g = menuItem.group;
1283            if(g){
1284                groups[g].remove(menuItem);
1285                menuItem.un("beforecheckchange", onBeforeCheck);
1286            }
1287        }
1288    };
1289 }();/*
1290  * - LGPL
1291  *
1292  * menu
1293  * 
1294  */
1295
1296 /**
1297  * @class Roo.bootstrap.Menu
1298  * @extends Roo.bootstrap.Component
1299  * Bootstrap Menu class - container for MenuItems
1300  * @cfg {String} type type of menu
1301  * 
1302  * @constructor
1303  * Create a new Menu
1304  * @param {Object} config The config object
1305  */
1306
1307
1308 Roo.bootstrap.Menu = function(config){
1309     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1310     if (this.registerMenu) {
1311         Roo.bootstrap.MenuMgr.register(this);
1312     }
1313     this.addEvents({
1314         /**
1315          * @event beforeshow
1316          * Fires before this menu is displayed
1317          * @param {Roo.menu.Menu} this
1318          */
1319         beforeshow : true,
1320         /**
1321          * @event beforehide
1322          * Fires before this menu is hidden
1323          * @param {Roo.menu.Menu} this
1324          */
1325         beforehide : true,
1326         /**
1327          * @event show
1328          * Fires after this menu is displayed
1329          * @param {Roo.menu.Menu} this
1330          */
1331         show : true,
1332         /**
1333          * @event hide
1334          * Fires after this menu is hidden
1335          * @param {Roo.menu.Menu} this
1336          */
1337         hide : true,
1338         /**
1339          * @event click
1340          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1341          * @param {Roo.menu.Menu} this
1342          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1343          * @param {Roo.EventObject} e
1344          */
1345         click : true,
1346         /**
1347          * @event mouseover
1348          * Fires when the mouse is hovering over this menu
1349          * @param {Roo.menu.Menu} this
1350          * @param {Roo.EventObject} e
1351          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1352          */
1353         mouseover : true,
1354         /**
1355          * @event mouseout
1356          * Fires when the mouse exits this menu
1357          * @param {Roo.menu.Menu} this
1358          * @param {Roo.EventObject} e
1359          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1360          */
1361         mouseout : true,
1362         /**
1363          * @event itemclick
1364          * Fires when a menu item contained in this menu is clicked
1365          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1366          * @param {Roo.EventObject} e
1367          */
1368         itemclick: true
1369     });
1370     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1371 };
1372
1373 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1374     
1375    /// html : false,
1376     //align : '',
1377     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1378     type: false,
1379     /**
1380      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1381      */
1382     registerMenu : true,
1383     
1384     menuItems :false, // stores the menu items..
1385     
1386     hidden:true,
1387     
1388     parentMenu : false,
1389     
1390     getChildContainer : function() {
1391         return this.el;  
1392     },
1393     
1394     getAutoCreate : function(){
1395          
1396         //if (['right'].indexOf(this.align)!==-1) {
1397         //    cfg.cn[1].cls += ' pull-right'
1398         //}
1399         var cfg = {
1400             tag : 'ul',
1401             cls : 'dropdown-menu' ,
1402             style : 'z-index:1000'
1403             
1404         }
1405         
1406         if (this.type === 'submenu') {
1407             cfg.cls = 'submenu active'
1408         }
1409         
1410         return cfg;
1411     },
1412     initEvents : function() {
1413         
1414        // Roo.log("ADD event");
1415        // Roo.log(this.triggerEl.dom);
1416         this.triggerEl.on('click', this.onTriggerPress, this);
1417         this.triggerEl.addClass('dropdown-toggle');
1418         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1419
1420         this.el.on("mouseover", this.onMouseOver, this);
1421         this.el.on("mouseout", this.onMouseOut, this);
1422         
1423         
1424     },
1425     findTargetItem : function(e){
1426         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1427         if(!t){
1428             return false;
1429         }
1430         //Roo.log(t);         Roo.log(t.id);
1431         if(t && t.id){
1432             //Roo.log(this.menuitems);
1433             return this.menuitems.get(t.id);
1434             
1435             //return this.items.get(t.menuItemId);
1436         }
1437         
1438         return false;
1439     },
1440     onClick : function(e){
1441         Roo.log("menu.onClick");
1442         var t = this.findTargetItem(e);
1443         if(!t){
1444             return;
1445         }
1446         Roo.log(e);
1447         /*
1448         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1449             if(t == this.activeItem && t.shouldDeactivate(e)){
1450                 this.activeItem.deactivate();
1451                 delete this.activeItem;
1452                 return;
1453             }
1454             if(t.canActivate){
1455                 this.setActiveItem(t, true);
1456             }
1457             return;
1458             
1459             
1460         }
1461         */
1462         Roo.log('pass click event');
1463         
1464         t.onClick(e);
1465         
1466         this.fireEvent("click", this, t, e);
1467         
1468         this.hide();
1469     },
1470      onMouseOver : function(e){
1471         var t  = this.findTargetItem(e);
1472         //Roo.log(t);
1473         //if(t){
1474         //    if(t.canActivate && !t.disabled){
1475         //        this.setActiveItem(t, true);
1476         //    }
1477         //}
1478         
1479         this.fireEvent("mouseover", this, e, t);
1480     },
1481     isVisible : function(){
1482         return !this.hidden;
1483     },
1484      onMouseOut : function(e){
1485         var t  = this.findTargetItem(e);
1486         
1487         //if(t ){
1488         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1489         //        this.activeItem.deactivate();
1490         //        delete this.activeItem;
1491         //    }
1492         //}
1493         this.fireEvent("mouseout", this, e, t);
1494     },
1495     
1496     
1497     /**
1498      * Displays this menu relative to another element
1499      * @param {String/HTMLElement/Roo.Element} element The element to align to
1500      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1501      * the element (defaults to this.defaultAlign)
1502      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1503      */
1504     show : function(el, pos, parentMenu){
1505         this.parentMenu = parentMenu;
1506         if(!this.el){
1507             this.render();
1508         }
1509         this.fireEvent("beforeshow", this);
1510         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1511     },
1512      /**
1513      * Displays this menu at a specific xy position
1514      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1515      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1516      */
1517     showAt : function(xy, parentMenu, /* private: */_e){
1518         this.parentMenu = parentMenu;
1519         if(!this.el){
1520             this.render();
1521         }
1522         if(_e !== false){
1523             this.fireEvent("beforeshow", this);
1524             
1525             //xy = this.el.adjustForConstraints(xy);
1526         }
1527         //this.el.setXY(xy);
1528         //this.el.show();
1529         this.hideMenuItems();
1530         this.hidden = false;
1531         this.triggerEl.addClass('open');
1532         this.focus();
1533         this.fireEvent("show", this);
1534     },
1535     
1536     focus : function(){
1537         return;
1538         if(!this.hidden){
1539             this.doFocus.defer(50, this);
1540         }
1541     },
1542
1543     doFocus : function(){
1544         if(!this.hidden){
1545             this.focusEl.focus();
1546         }
1547     },
1548
1549     /**
1550      * Hides this menu and optionally all parent menus
1551      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1552      */
1553     hide : function(deep){
1554         
1555         this.hideMenuItems();
1556         if(this.el && this.isVisible()){
1557             this.fireEvent("beforehide", this);
1558             if(this.activeItem){
1559                 this.activeItem.deactivate();
1560                 this.activeItem = null;
1561             }
1562             this.triggerEl.removeClass('open');;
1563             this.hidden = true;
1564             this.fireEvent("hide", this);
1565         }
1566         if(deep === true && this.parentMenu){
1567             this.parentMenu.hide(true);
1568         }
1569     },
1570     
1571     onTriggerPress  : function(e)
1572     {
1573         
1574         Roo.log('trigger press');
1575         //Roo.log(e.getTarget());
1576        // Roo.log(this.triggerEl.dom);
1577         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1578             return;
1579         }
1580         if (this.isVisible()) {
1581             Roo.log('hide');
1582             this.hide();
1583         } else {
1584             this.show(this.triggerEl, false, false);
1585         }
1586         
1587         
1588     },
1589     
1590          
1591        
1592     
1593     hideMenuItems : function()
1594     {
1595         //$(backdrop).remove()
1596         Roo.select('.open',true).each(function(aa) {
1597             
1598             aa.removeClass('open');
1599           //var parent = getParent($(this))
1600           //var relatedTarget = { relatedTarget: this }
1601           
1602            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1603           //if (e.isDefaultPrevented()) return
1604            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1605         })
1606     },
1607     addxtypeChild : function (tree, cntr) {
1608         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1609           
1610         this.menuitems.add(comp);
1611         return comp;
1612
1613     },
1614     getEl : function()
1615     {
1616         Roo.log(this.el);
1617         return this.el;
1618     }
1619 });
1620
1621  
1622  /*
1623  * - LGPL
1624  *
1625  * menu item
1626  * 
1627  */
1628
1629
1630 /**
1631  * @class Roo.bootstrap.MenuItem
1632  * @extends Roo.bootstrap.Component
1633  * Bootstrap MenuItem class
1634  * @cfg {String} html the menu label
1635  * @cfg {String} href the link
1636  * @cfg {Boolean} preventDefault (true | false) default true
1637  * 
1638  * 
1639  * @constructor
1640  * Create a new MenuItem
1641  * @param {Object} config The config object
1642  */
1643
1644
1645 Roo.bootstrap.MenuItem = function(config){
1646     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1647     this.addEvents({
1648         // raw events
1649         /**
1650          * @event click
1651          * The raw click event for the entire grid.
1652          * @param {Roo.EventObject} e
1653          */
1654         "click" : true
1655     });
1656 };
1657
1658 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1659     
1660     href : false,
1661     html : false,
1662     preventDefault: true,
1663     
1664     getAutoCreate : function(){
1665         var cfg= {
1666             tag: 'li',
1667         cls: 'dropdown-menu-item',
1668             cn: [
1669             {
1670                 tag : 'a',
1671                 href : '#',
1672                 html : 'Link'
1673             }
1674             ]
1675     };
1676         
1677         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1678         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1679         return cfg;
1680     },
1681     
1682     initEvents: function() {
1683         
1684         //this.el.select('a').on('click', this.onClick, this);
1685         
1686     },
1687     onClick : function(e)
1688     {
1689         Roo.log('item on click ');
1690         //if(this.preventDefault){
1691         //    e.preventDefault();
1692         //}
1693         //this.parent().hideMenuItems();
1694         
1695         this.fireEvent('click', this, e);
1696     },
1697     getEl : function()
1698     {
1699         return this.el;
1700     }
1701 });
1702
1703  
1704
1705  /*
1706  * - LGPL
1707  *
1708  * menu separator
1709  * 
1710  */
1711
1712
1713 /**
1714  * @class Roo.bootstrap.MenuSeparator
1715  * @extends Roo.bootstrap.Component
1716  * Bootstrap MenuSeparator class
1717  * 
1718  * @constructor
1719  * Create a new MenuItem
1720  * @param {Object} config The config object
1721  */
1722
1723
1724 Roo.bootstrap.MenuSeparator = function(config){
1725     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1726 };
1727
1728 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1729     
1730     getAutoCreate : function(){
1731         var cfg = {
1732             cls: 'divider',
1733             tag : 'li'
1734         };
1735         
1736         return cfg;
1737     }
1738    
1739 });
1740
1741  
1742
1743  
1744 /*
1745 <div class="modal fade">
1746   <div class="modal-dialog">
1747     <div class="modal-content">
1748       <div class="modal-header">
1749         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1750         <h4 class="modal-title">Modal title</h4>
1751       </div>
1752       <div class="modal-body">
1753         <p>One fine body&hellip;</p>
1754       </div>
1755       <div class="modal-footer">
1756         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1757         <button type="button" class="btn btn-primary">Save changes</button>
1758       </div>
1759     </div><!-- /.modal-content -->
1760   </div><!-- /.modal-dialog -->
1761 </div><!-- /.modal -->
1762 */
1763 /*
1764  * - LGPL
1765  *
1766  * page contgainer.
1767  * 
1768  */
1769
1770 /**
1771  * @class Roo.bootstrap.Modal
1772  * @extends Roo.bootstrap.Component
1773  * Bootstrap Modal class
1774  * @cfg {String} title Title of dialog
1775  * @cfg {Boolean} specificTitle (true|false) default false
1776  * @cfg {Array} buttons Array of buttons or standard button set..
1777  * @cfg {String} buttonPosition (left|right|center) default right
1778  * 
1779  * @constructor
1780  * Create a new Modal Dialog
1781  * @param {Object} config The config object
1782  */
1783
1784 Roo.bootstrap.Modal = function(config){
1785     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1786     this.addEvents({
1787         // raw events
1788         /**
1789          * @event btnclick
1790          * The raw btnclick event for the button
1791          * @param {Roo.EventObject} e
1792          */
1793         "btnclick" : true
1794     });
1795     this.buttons = this.buttons || [];
1796 };
1797
1798 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1799     
1800     title : 'test dialog',
1801    
1802     buttons : false,
1803     
1804     // set on load...
1805     body:  false,
1806     
1807     specificTitle: false,
1808     
1809     buttonPosition: 'right',
1810     
1811     onRender : function(ct, position)
1812     {
1813         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1814      
1815         if(!this.el){
1816             var cfg = Roo.apply({},  this.getAutoCreate());
1817             cfg.id = Roo.id();
1818             //if(!cfg.name){
1819             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1820             //}
1821             //if (!cfg.name.length) {
1822             //    delete cfg.name;
1823            // }
1824             if (this.cls) {
1825                 cfg.cls += ' ' + this.cls;
1826             }
1827             if (this.style) {
1828                 cfg.style = this.style;
1829             }
1830             this.el = Roo.get(document.body).createChild(cfg, position);
1831         }
1832         //var type = this.el.dom.type;
1833         
1834         if(this.tabIndex !== undefined){
1835             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1836         }
1837         
1838         
1839         
1840         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1841         this.maskEl.enableDisplayMode("block");
1842         this.maskEl.hide();
1843         //this.el.addClass("x-dlg-modal");
1844     
1845         if (this.buttons.length) {
1846             Roo.each(this.buttons, function(bb) {
1847                 b = Roo.apply({}, bb);
1848                 b.xns = b.xns || Roo.bootstrap;
1849                 b.xtype = b.xtype || 'Button';
1850                 if (typeof(b.listeners) == 'undefined') {
1851                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1852                 }
1853                 
1854                 var btn = Roo.factory(b);
1855                 
1856                 btn.onRender(this.el.select('.modal-footer div').first());
1857                 
1858             },this);
1859         }
1860         // render the children.
1861         var nitems = [];
1862         
1863         if(typeof(this.items) != 'undefined'){
1864             var items = this.items;
1865             delete this.items;
1866
1867             for(var i =0;i < items.length;i++) {
1868                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1869             }
1870         }
1871         
1872         this.items = nitems;
1873         
1874         this.body = this.el.select('.modal-body',true).first();
1875         this.close = this.el.select('.modal-header .close', true).first();
1876         this.footer = this.el.select('.modal-footer',true).first();
1877         this.initEvents();
1878         //this.el.addClass([this.fieldClass, this.cls]);
1879         
1880     },
1881     getAutoCreate : function(){
1882         
1883         
1884         var bdy = {
1885                 cls : 'modal-body',
1886                 html : this.html || ''
1887         };
1888         
1889         var title = {
1890             tag: 'h4',
1891             cls : 'modal-title',
1892             html : this.title
1893         };
1894         
1895         if(this.specificTitle){
1896             title = this.title;
1897         };
1898         
1899         return modal = {
1900             cls: "modal fade",
1901             style : 'display: none',
1902             cn : [
1903                 {
1904                     cls: "modal-dialog",
1905                     cn : [
1906                         {
1907                             cls : "modal-content",
1908                             cn : [
1909                                 {
1910                                     cls : 'modal-header',
1911                                     cn : [
1912                                         {
1913                                             tag: 'button',
1914                                             cls : 'close',
1915                                             html : '&times'
1916                                         },
1917                                         title
1918                                     ]
1919                                 },
1920                                 bdy,
1921                                 {
1922                                     cls : 'modal-footer',
1923                                     cn : [
1924                                         {
1925                                             tag: 'div',
1926                                             cls: 'btn-' + this.buttonPosition
1927                                         }
1928                                     ]
1929                                     
1930                                 }
1931                                 
1932                                 
1933                             ]
1934                             
1935                         }
1936                     ]
1937                         
1938                 }
1939             ]
1940             
1941             
1942         };
1943           
1944     },
1945     getChildContainer : function() {
1946          
1947          return this.el.select('.modal-body',true).first();
1948         
1949     },
1950     getButtonContainer : function() {
1951          return this.el.select('.modal-footer div',true).first();
1952         
1953     },
1954     initEvents : function()
1955     {
1956         this.el.select('.modal-header .close').on('click', this.hide, this);
1957 //        
1958 //        this.addxtype(this);
1959     },
1960     show : function() {
1961         
1962         if (!this.rendered) {
1963             this.render();
1964         }
1965        
1966         this.el.addClass('on');
1967         this.el.removeClass('fade');
1968         this.el.setStyle('display', 'block');
1969         Roo.get(document.body).addClass("x-body-masked");
1970         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1971         this.maskEl.show();
1972         this.el.setStyle('zIndex', '10001');
1973         this.fireEvent('show', this);
1974         
1975         
1976     },
1977     hide : function()
1978     {
1979         Roo.log('Modal hide?!');
1980         this.maskEl.hide();
1981         Roo.get(document.body).removeClass("x-body-masked");
1982         this.el.removeClass('on');
1983         this.el.addClass('fade');
1984         this.el.setStyle('display', 'none');
1985         this.fireEvent('hide', this);
1986     },
1987     
1988     addButton : function(str, cb)
1989     {
1990          
1991         
1992         var b = Roo.apply({}, { html : str } );
1993         b.xns = b.xns || Roo.bootstrap;
1994         b.xtype = b.xtype || 'Button';
1995         if (typeof(b.listeners) == 'undefined') {
1996             b.listeners = { click : cb.createDelegate(this)  };
1997         }
1998         
1999         var btn = Roo.factory(b);
2000            
2001         btn.onRender(this.el.select('.modal-footer div').first());
2002         
2003         return btn;   
2004        
2005     },
2006     
2007     setDefaultButton : function(btn)
2008     {
2009         //this.el.select('.modal-footer').()
2010     },
2011     resizeTo: function(w,h)
2012     {
2013         // skip..
2014     },
2015     setContentSize  : function(w, h)
2016     {
2017         
2018     },
2019     onButtonClick: function(btn,e)
2020     {
2021         //Roo.log([a,b,c]);
2022         this.fireEvent('btnclick', btn.name, e);
2023     },
2024     setTitle: function(str) {
2025         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2026         
2027     }
2028 });
2029
2030
2031 Roo.apply(Roo.bootstrap.Modal,  {
2032     /**
2033          * Button config that displays a single OK button
2034          * @type Object
2035          */
2036         OK :  [{
2037             name : 'ok',
2038             weight : 'primary',
2039             html : 'OK'
2040         }], 
2041         /**
2042          * Button config that displays Yes and No buttons
2043          * @type Object
2044          */
2045         YESNO : [
2046             {
2047                 name  : 'no',
2048                 html : 'No'
2049             },
2050             {
2051                 name  :'yes',
2052                 weight : 'primary',
2053                 html : 'Yes'
2054             }
2055         ],
2056         
2057         /**
2058          * Button config that displays OK and Cancel buttons
2059          * @type Object
2060          */
2061         OKCANCEL : [
2062             {
2063                name : 'cancel',
2064                 html : 'Cancel'
2065             },
2066             {
2067                 name : 'ok',
2068                 weight : 'primary',
2069                 html : 'OK'
2070             }
2071         ],
2072         /**
2073          * Button config that displays Yes, No and Cancel buttons
2074          * @type Object
2075          */
2076         YESNOCANCEL : [
2077             {
2078                 name : 'yes',
2079                 weight : 'primary',
2080                 html : 'Yes'
2081             },
2082             {
2083                 name : 'no',
2084                 html : 'No'
2085             },
2086             {
2087                 name : 'cancel',
2088                 html : 'Cancel'
2089             }
2090         ]
2091 });
2092  /*
2093  * - LGPL
2094  *
2095  * messagebox - can be used as a replace
2096  * 
2097  */
2098 /**
2099  * @class Roo.MessageBox
2100  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2101  * Example usage:
2102  *<pre><code>
2103 // Basic alert:
2104 Roo.Msg.alert('Status', 'Changes saved successfully.');
2105
2106 // Prompt for user data:
2107 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2108     if (btn == 'ok'){
2109         // process text value...
2110     }
2111 });
2112
2113 // Show a dialog using config options:
2114 Roo.Msg.show({
2115    title:'Save Changes?',
2116    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2117    buttons: Roo.Msg.YESNOCANCEL,
2118    fn: processResult,
2119    animEl: 'elId'
2120 });
2121 </code></pre>
2122  * @singleton
2123  */
2124 Roo.bootstrap.MessageBox = function(){
2125     var dlg, opt, mask, waitTimer;
2126     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2127     var buttons, activeTextEl, bwidth;
2128
2129     
2130     // private
2131     var handleButton = function(button){
2132         dlg.hide();
2133         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2134     };
2135
2136     // private
2137     var handleHide = function(){
2138         if(opt && opt.cls){
2139             dlg.el.removeClass(opt.cls);
2140         }
2141         //if(waitTimer){
2142         //    Roo.TaskMgr.stop(waitTimer);
2143         //    waitTimer = null;
2144         //}
2145     };
2146
2147     // private
2148     var updateButtons = function(b){
2149         var width = 0;
2150         if(!b){
2151             buttons["ok"].hide();
2152             buttons["cancel"].hide();
2153             buttons["yes"].hide();
2154             buttons["no"].hide();
2155             //dlg.footer.dom.style.display = 'none';
2156             return width;
2157         }
2158         dlg.footer.dom.style.display = '';
2159         for(var k in buttons){
2160             if(typeof buttons[k] != "function"){
2161                 if(b[k]){
2162                     buttons[k].show();
2163                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2164                     width += buttons[k].el.getWidth()+15;
2165                 }else{
2166                     buttons[k].hide();
2167                 }
2168             }
2169         }
2170         return width;
2171     };
2172
2173     // private
2174     var handleEsc = function(d, k, e){
2175         if(opt && opt.closable !== false){
2176             dlg.hide();
2177         }
2178         if(e){
2179             e.stopEvent();
2180         }
2181     };
2182
2183     return {
2184         /**
2185          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2186          * @return {Roo.BasicDialog} The BasicDialog element
2187          */
2188         getDialog : function(){
2189            if(!dlg){
2190                 dlg = new Roo.bootstrap.Modal( {
2191                     //draggable: true,
2192                     //resizable:false,
2193                     //constraintoviewport:false,
2194                     //fixedcenter:true,
2195                     //collapsible : false,
2196                     //shim:true,
2197                     //modal: true,
2198                   //  width:400,
2199                   //  height:100,
2200                     //buttonAlign:"center",
2201                     closeClick : function(){
2202                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2203                             handleButton("no");
2204                         }else{
2205                             handleButton("cancel");
2206                         }
2207                     }
2208                 });
2209                 dlg.render();
2210                 dlg.on("hide", handleHide);
2211                 mask = dlg.mask;
2212                 //dlg.addKeyListener(27, handleEsc);
2213                 buttons = {};
2214                 this.buttons = buttons;
2215                 var bt = this.buttonText;
2216                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2217                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2218                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2219                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2220                 Roo.log(buttons)
2221                 bodyEl = dlg.body.createChild({
2222
2223                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2224                         '<textarea class="roo-mb-textarea"></textarea>' +
2225                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2226                 });
2227                 msgEl = bodyEl.dom.firstChild;
2228                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2229                 textboxEl.enableDisplayMode();
2230                 textboxEl.addKeyListener([10,13], function(){
2231                     if(dlg.isVisible() && opt && opt.buttons){
2232                         if(opt.buttons.ok){
2233                             handleButton("ok");
2234                         }else if(opt.buttons.yes){
2235                             handleButton("yes");
2236                         }
2237                     }
2238                 });
2239                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2240                 textareaEl.enableDisplayMode();
2241                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2242                 progressEl.enableDisplayMode();
2243                 var pf = progressEl.dom.firstChild;
2244                 if (pf) {
2245                     pp = Roo.get(pf.firstChild);
2246                     pp.setHeight(pf.offsetHeight);
2247                 }
2248                 
2249             }
2250             return dlg;
2251         },
2252
2253         /**
2254          * Updates the message box body text
2255          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2256          * the XHTML-compliant non-breaking space character '&amp;#160;')
2257          * @return {Roo.MessageBox} This message box
2258          */
2259         updateText : function(text){
2260             if(!dlg.isVisible() && !opt.width){
2261                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2262             }
2263             msgEl.innerHTML = text || '&#160;';
2264       
2265             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2266             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2267             var w = Math.max(
2268                     Math.min(opt.width || cw , this.maxWidth), 
2269                     Math.max(opt.minWidth || this.minWidth, bwidth)
2270             );
2271             if(opt.prompt){
2272                 activeTextEl.setWidth(w);
2273             }
2274             if(dlg.isVisible()){
2275                 dlg.fixedcenter = false;
2276             }
2277             // to big, make it scroll. = But as usual stupid IE does not support
2278             // !important..
2279             
2280             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2281                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2282                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2283             } else {
2284                 bodyEl.dom.style.height = '';
2285                 bodyEl.dom.style.overflowY = '';
2286             }
2287             if (cw > w) {
2288                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2289             } else {
2290                 bodyEl.dom.style.overflowX = '';
2291             }
2292             
2293             dlg.setContentSize(w, bodyEl.getHeight());
2294             if(dlg.isVisible()){
2295                 dlg.fixedcenter = true;
2296             }
2297             return this;
2298         },
2299
2300         /**
2301          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2302          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2303          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2304          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2305          * @return {Roo.MessageBox} This message box
2306          */
2307         updateProgress : function(value, text){
2308             if(text){
2309                 this.updateText(text);
2310             }
2311             if (pp) { // weird bug on my firefox - for some reason this is not defined
2312                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2313             }
2314             return this;
2315         },        
2316
2317         /**
2318          * Returns true if the message box is currently displayed
2319          * @return {Boolean} True if the message box is visible, else false
2320          */
2321         isVisible : function(){
2322             return dlg && dlg.isVisible();  
2323         },
2324
2325         /**
2326          * Hides the message box if it is displayed
2327          */
2328         hide : function(){
2329             if(this.isVisible()){
2330                 dlg.hide();
2331             }  
2332         },
2333
2334         /**
2335          * Displays a new message box, or reinitializes an existing message box, based on the config options
2336          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2337          * The following config object properties are supported:
2338          * <pre>
2339 Property    Type             Description
2340 ----------  ---------------  ------------------------------------------------------------------------------------
2341 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2342                                    closes (defaults to undefined)
2343 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2344                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2345 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2346                                    progress and wait dialogs will ignore this property and always hide the
2347                                    close button as they can only be closed programmatically.
2348 cls               String           A custom CSS class to apply to the message box element
2349 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2350                                    displayed (defaults to 75)
2351 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2352                                    function will be btn (the name of the button that was clicked, if applicable,
2353                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2354                                    Progress and wait dialogs will ignore this option since they do not respond to
2355                                    user actions and can only be closed programmatically, so any required function
2356                                    should be called by the same code after it closes the dialog.
2357 icon              String           A CSS class that provides a background image to be used as an icon for
2358                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2359 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2360 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2361 modal             Boolean          False to allow user interaction with the page while the message box is
2362                                    displayed (defaults to true)
2363 msg               String           A string that will replace the existing message box body text (defaults
2364                                    to the XHTML-compliant non-breaking space character '&#160;')
2365 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2366 progress          Boolean          True to display a progress bar (defaults to false)
2367 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2368 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2369 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2370 title             String           The title text
2371 value             String           The string value to set into the active textbox element if displayed
2372 wait              Boolean          True to display a progress bar (defaults to false)
2373 width             Number           The width of the dialog in pixels
2374 </pre>
2375          *
2376          * Example usage:
2377          * <pre><code>
2378 Roo.Msg.show({
2379    title: 'Address',
2380    msg: 'Please enter your address:',
2381    width: 300,
2382    buttons: Roo.MessageBox.OKCANCEL,
2383    multiline: true,
2384    fn: saveAddress,
2385    animEl: 'addAddressBtn'
2386 });
2387 </code></pre>
2388          * @param {Object} config Configuration options
2389          * @return {Roo.MessageBox} This message box
2390          */
2391         show : function(options)
2392         {
2393             
2394             // this causes nightmares if you show one dialog after another
2395             // especially on callbacks..
2396              
2397             if(this.isVisible()){
2398                 
2399                 this.hide();
2400                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2401                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2402                 Roo.log("New Dialog Message:" +  options.msg )
2403                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2404                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2405                 
2406             }
2407             var d = this.getDialog();
2408             opt = options;
2409             d.setTitle(opt.title || "&#160;");
2410             d.close.setDisplayed(opt.closable !== false);
2411             activeTextEl = textboxEl;
2412             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2413             if(opt.prompt){
2414                 if(opt.multiline){
2415                     textboxEl.hide();
2416                     textareaEl.show();
2417                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2418                         opt.multiline : this.defaultTextHeight);
2419                     activeTextEl = textareaEl;
2420                 }else{
2421                     textboxEl.show();
2422                     textareaEl.hide();
2423                 }
2424             }else{
2425                 textboxEl.hide();
2426                 textareaEl.hide();
2427             }
2428             progressEl.setDisplayed(opt.progress === true);
2429             this.updateProgress(0);
2430             activeTextEl.dom.value = opt.value || "";
2431             if(opt.prompt){
2432                 dlg.setDefaultButton(activeTextEl);
2433             }else{
2434                 var bs = opt.buttons;
2435                 var db = null;
2436                 if(bs && bs.ok){
2437                     db = buttons["ok"];
2438                 }else if(bs && bs.yes){
2439                     db = buttons["yes"];
2440                 }
2441                 dlg.setDefaultButton(db);
2442             }
2443             bwidth = updateButtons(opt.buttons);
2444             this.updateText(opt.msg);
2445             if(opt.cls){
2446                 d.el.addClass(opt.cls);
2447             }
2448             d.proxyDrag = opt.proxyDrag === true;
2449             d.modal = opt.modal !== false;
2450             d.mask = opt.modal !== false ? mask : false;
2451             if(!d.isVisible()){
2452                 // force it to the end of the z-index stack so it gets a cursor in FF
2453                 document.body.appendChild(dlg.el.dom);
2454                 d.animateTarget = null;
2455                 d.show(options.animEl);
2456             }
2457             return this;
2458         },
2459
2460         /**
2461          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2462          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2463          * and closing the message box when the process is complete.
2464          * @param {String} title The title bar text
2465          * @param {String} msg The message box body text
2466          * @return {Roo.MessageBox} This message box
2467          */
2468         progress : function(title, msg){
2469             this.show({
2470                 title : title,
2471                 msg : msg,
2472                 buttons: false,
2473                 progress:true,
2474                 closable:false,
2475                 minWidth: this.minProgressWidth,
2476                 modal : true
2477             });
2478             return this;
2479         },
2480
2481         /**
2482          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2483          * If a callback function is passed it will be called after the user clicks the button, and the
2484          * id of the button that was clicked will be passed as the only parameter to the callback
2485          * (could also be the top-right close button).
2486          * @param {String} title The title bar text
2487          * @param {String} msg The message box body text
2488          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2489          * @param {Object} scope (optional) The scope of the callback function
2490          * @return {Roo.MessageBox} This message box
2491          */
2492         alert : function(title, msg, fn, scope){
2493             this.show({
2494                 title : title,
2495                 msg : msg,
2496                 buttons: this.OK,
2497                 fn: fn,
2498                 scope : scope,
2499                 modal : true
2500             });
2501             return this;
2502         },
2503
2504         /**
2505          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2506          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2507          * You are responsible for closing the message box when the process is complete.
2508          * @param {String} msg The message box body text
2509          * @param {String} title (optional) The title bar text
2510          * @return {Roo.MessageBox} This message box
2511          */
2512         wait : function(msg, title){
2513             this.show({
2514                 title : title,
2515                 msg : msg,
2516                 buttons: false,
2517                 closable:false,
2518                 progress:true,
2519                 modal:true,
2520                 width:300,
2521                 wait:true
2522             });
2523             waitTimer = Roo.TaskMgr.start({
2524                 run: function(i){
2525                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2526                 },
2527                 interval: 1000
2528             });
2529             return this;
2530         },
2531
2532         /**
2533          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2534          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2535          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2536          * @param {String} title The title bar text
2537          * @param {String} msg The message box body text
2538          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2539          * @param {Object} scope (optional) The scope of the callback function
2540          * @return {Roo.MessageBox} This message box
2541          */
2542         confirm : function(title, msg, fn, scope){
2543             this.show({
2544                 title : title,
2545                 msg : msg,
2546                 buttons: this.YESNO,
2547                 fn: fn,
2548                 scope : scope,
2549                 modal : true
2550             });
2551             return this;
2552         },
2553
2554         /**
2555          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2556          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2557          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2558          * (could also be the top-right close button) and the text that was entered will be passed as the two
2559          * parameters to the callback.
2560          * @param {String} title The title bar text
2561          * @param {String} msg The message box body text
2562          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2563          * @param {Object} scope (optional) The scope of the callback function
2564          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2565          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2566          * @return {Roo.MessageBox} This message box
2567          */
2568         prompt : function(title, msg, fn, scope, multiline){
2569             this.show({
2570                 title : title,
2571                 msg : msg,
2572                 buttons: this.OKCANCEL,
2573                 fn: fn,
2574                 minWidth:250,
2575                 scope : scope,
2576                 prompt:true,
2577                 multiline: multiline,
2578                 modal : true
2579             });
2580             return this;
2581         },
2582
2583         /**
2584          * Button config that displays a single OK button
2585          * @type Object
2586          */
2587         OK : {ok:true},
2588         /**
2589          * Button config that displays Yes and No buttons
2590          * @type Object
2591          */
2592         YESNO : {yes:true, no:true},
2593         /**
2594          * Button config that displays OK and Cancel buttons
2595          * @type Object
2596          */
2597         OKCANCEL : {ok:true, cancel:true},
2598         /**
2599          * Button config that displays Yes, No and Cancel buttons
2600          * @type Object
2601          */
2602         YESNOCANCEL : {yes:true, no:true, cancel:true},
2603
2604         /**
2605          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2606          * @type Number
2607          */
2608         defaultTextHeight : 75,
2609         /**
2610          * The maximum width in pixels of the message box (defaults to 600)
2611          * @type Number
2612          */
2613         maxWidth : 600,
2614         /**
2615          * The minimum width in pixels of the message box (defaults to 100)
2616          * @type Number
2617          */
2618         minWidth : 100,
2619         /**
2620          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2621          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2622          * @type Number
2623          */
2624         minProgressWidth : 250,
2625         /**
2626          * An object containing the default button text strings that can be overriden for localized language support.
2627          * Supported properties are: ok, cancel, yes and no.
2628          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2629          * @type Object
2630          */
2631         buttonText : {
2632             ok : "OK",
2633             cancel : "Cancel",
2634             yes : "Yes",
2635             no : "No"
2636         }
2637     };
2638 }();
2639
2640 /**
2641  * Shorthand for {@link Roo.MessageBox}
2642  */
2643 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2644 Roo.Msg = Roo.Msg || Roo.MessageBox;
2645 /*
2646  * - LGPL
2647  *
2648  * navbar
2649  * 
2650  */
2651
2652 /**
2653  * @class Roo.bootstrap.Navbar
2654  * @extends Roo.bootstrap.Component
2655  * Bootstrap Navbar class
2656
2657  * @constructor
2658  * Create a new Navbar
2659  * @param {Object} config The config object
2660  */
2661
2662
2663 Roo.bootstrap.Navbar = function(config){
2664     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2665     
2666 };
2667
2668 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2669     
2670     
2671    
2672     // private
2673     navItems : false,
2674     loadMask : false,
2675     
2676     
2677     getAutoCreate : function(){
2678         
2679         
2680         throw { message : "nav bar is now a abstract base class - use SimpleBar / HeaderBar / SideBar etc..."};
2681         
2682     },
2683     
2684     initEvents :function ()
2685     {
2686         //Roo.log(this.el.select('.navbar-toggle',true));
2687         this.el.select('.navbar-toggle',true).on('click', function() {
2688            // Roo.log('click');
2689             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2690         }, this);
2691         
2692         var mark = {
2693             tag: "div",
2694             cls:"x-dlg-mask"
2695         }
2696         
2697         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2698         
2699         var size = this.el.getSize();
2700         this.maskEl.setSize(size.width, size.height);
2701         this.maskEl.enableDisplayMode("block");
2702         this.maskEl.hide();
2703         
2704         if(this.loadMask){
2705             this.maskEl.show();
2706         }
2707     },
2708     
2709     
2710     getChildContainer : function()
2711     {
2712         if (this.el.select('.collapse').getCount()) {
2713             return this.el.select('.collapse',true).first();
2714         }
2715         
2716         return this.el;
2717     },
2718     
2719     mask : function()
2720     {
2721         this.maskEl.show();
2722     },
2723     
2724     unmask : function()
2725     {
2726         this.maskEl.hide();
2727     }
2728     
2729     
2730     
2731 });
2732
2733
2734
2735  
2736
2737  /*
2738  * - LGPL
2739  *
2740  * navbar
2741  * 
2742  */
2743
2744 /**
2745  * @class Roo.bootstrap.NavSimplebar
2746  * @extends Roo.bootstrap.Navbar
2747  * Bootstrap Sidebar class
2748  *
2749  * @cfg {Boolean} inverse is inverted color
2750  * 
2751  * @cfg {String} type (nav | pills | tabs)
2752  * @cfg {Boolean} arrangement stacked | justified
2753  * @cfg {String} align (left | right) alignment
2754  * 
2755  * @cfg {Boolean} main (true|false) main nav bar? default false
2756  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2757  * 
2758  * @cfg {String} tag (header|footer|nav|div) default is nav 
2759
2760  * 
2761  * 
2762  * 
2763  * @constructor
2764  * Create a new Sidebar
2765  * @param {Object} config The config object
2766  */
2767
2768
2769 Roo.bootstrap.NavSimplebar = function(config){
2770     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2771 };
2772
2773 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2774     
2775     inverse: false,
2776     
2777     type: false,
2778     arrangement: '',
2779     align : false,
2780     
2781     
2782     
2783     main : false,
2784     
2785     
2786     tag : false,
2787     
2788     
2789     getAutoCreate : function(){
2790         
2791         
2792         var cfg = {
2793             tag : this.tag || 'div',
2794             cls : 'navbar'
2795         };
2796           
2797         
2798         cfg.cn = [
2799             {
2800                 cls: 'nav',
2801                 tag : 'ul'
2802             }
2803         ];
2804         
2805          
2806         this.type = this.type || 'nav';
2807         if (['tabs','pills'].indexOf(this.type)!==-1) {
2808             cfg.cn[0].cls += ' nav-' + this.type
2809         
2810         
2811         } else {
2812             if (this.type!=='nav') {
2813                 Roo.log('nav type must be nav/tabs/pills')
2814             }
2815             cfg.cn[0].cls += ' navbar-nav'
2816         }
2817         
2818         
2819         
2820         
2821         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2822             cfg.cn[0].cls += ' nav-' + this.arrangement;
2823         }
2824         
2825         
2826         if (this.align === 'right') {
2827             cfg.cn[0].cls += ' navbar-right';
2828         }
2829         
2830         if (this.inverse) {
2831             cfg.cls += ' navbar-inverse';
2832             
2833         }
2834         
2835         
2836         return cfg;
2837     
2838         
2839     }
2840     
2841     
2842     
2843 });
2844
2845
2846
2847  
2848
2849  
2850        /*
2851  * - LGPL
2852  *
2853  * navbar
2854  * 
2855  */
2856
2857 /**
2858  * @class Roo.bootstrap.NavHeaderbar
2859  * @extends Roo.bootstrap.NavSimplebar
2860  * Bootstrap Sidebar class
2861  *
2862  * @cfg {String} brand what is brand
2863  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2864  * @cfg {String} brand_href href of the brand
2865  * 
2866  * @constructor
2867  * Create a new Sidebar
2868  * @param {Object} config The config object
2869  */
2870
2871
2872 Roo.bootstrap.NavHeaderbar = function(config){
2873     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2874 };
2875
2876 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2877     
2878     position: '',
2879     brand: '',
2880     brand_href: false,
2881     
2882     
2883     getAutoCreate : function(){
2884         
2885         
2886         
2887         var   cfg = {
2888             tag: this.nav || 'nav',
2889             cls: 'navbar',
2890             role: 'navigation',
2891             cn: [
2892                 {
2893                     tag: 'div',
2894                     cls: 'navbar-header',
2895                     cn: [
2896                         {
2897                         tag: 'button',
2898                         type: 'button',
2899                         cls: 'navbar-toggle',
2900                         'data-toggle': 'collapse',
2901                         cn: [
2902                             {
2903                                 tag: 'span',
2904                                 cls: 'sr-only',
2905                                 html: 'Toggle navigation'
2906                             },
2907                             {
2908                                 tag: 'span',
2909                                 cls: 'icon-bar'
2910                             },
2911                             {
2912                                 tag: 'span',
2913                                 cls: 'icon-bar'
2914                             },
2915                             {
2916                                 tag: 'span',
2917                                 cls: 'icon-bar'
2918                             }
2919                         ]
2920                         }
2921                     ]
2922                 },
2923                 {
2924                 tag: 'div',
2925                 cls: 'collapse navbar-collapse'
2926                 }
2927             ]
2928         };
2929         
2930         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2931         
2932         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2933             cfg.cls += ' navbar-' + this.position;
2934             
2935             // tag can override this..
2936             
2937             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
2938         }
2939         
2940         if (this.brand !== '') {
2941             cfg.cn[0].cn.push({
2942                 tag: 'a',
2943                 href: this.brand_href ? this.brand_href : '#',
2944                 cls: 'navbar-brand',
2945                 cn: [
2946                 this.brand
2947                 ]
2948             });
2949         }
2950         
2951         if(this.main){
2952             cfg.cls += ' main-nav';
2953         }
2954         
2955         
2956         return cfg;
2957
2958         
2959     }
2960     
2961     
2962     
2963 });
2964
2965
2966
2967  
2968
2969  /*
2970  * - LGPL
2971  *
2972  * navbar
2973  * 
2974  */
2975
2976 /**
2977  * @class Roo.bootstrap.NavSidebar
2978  * @extends Roo.bootstrap.Navbar
2979  * Bootstrap Sidebar class
2980  * 
2981  * @constructor
2982  * Create a new Sidebar
2983  * @param {Object} config The config object
2984  */
2985
2986
2987 Roo.bootstrap.NavSidebar = function(config){
2988     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
2989 };
2990
2991 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
2992     
2993     
2994     getAutoCreate : function(){
2995         
2996         
2997         return  {
2998             tag: 'div',
2999             cls: 'sidebar-nav'
3000         };
3001     
3002         
3003     }
3004     
3005     
3006     
3007 });
3008
3009
3010
3011  
3012
3013  /*
3014  * - LGPL
3015  *
3016  * nav group
3017  * 
3018  */
3019
3020 /**
3021  * @class Roo.bootstrap.NavGroup
3022  * @extends Roo.bootstrap.Component
3023  * Bootstrap NavGroup class
3024  * @cfg {String} align left | right
3025  * @cfg {Boolean} inverse false | true
3026  * @cfg {String} type (nav|pills|tab) default nav
3027  * @cfg {String} navId - reference Id for navbar.
3028
3029  * 
3030  * @constructor
3031  * Create a new nav group
3032  * @param {Object} config The config object
3033  */
3034
3035 Roo.bootstrap.NavGroup = function(config){
3036     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3037     this.navItems = [];
3038     Roo.bootstrap.NavGroup.register(this);
3039      this.addEvents({
3040         /**
3041              * @event changed
3042              * Fires when the active item changes
3043              * @param {Roo.bootstrap.NavGroup} this
3044              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3045              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3046          */
3047         'changed': true
3048      });
3049     
3050 };
3051
3052 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3053     
3054     align: '',
3055     inverse: false,
3056     form: false,
3057     type: 'nav',
3058     navId : '',
3059     // private
3060     
3061     navItems : false,
3062     
3063     getAutoCreate : function()
3064     {
3065         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3066         
3067         cfg = {
3068             tag : 'ul',
3069             cls: 'nav' 
3070         }
3071         
3072         if (['tabs','pills'].indexOf(this.type)!==-1) {
3073             cfg.cls += ' nav-' + this.type
3074         } else {
3075             if (this.type!=='nav') {
3076                 Roo.log('nav type must be nav/tabs/pills')
3077             }
3078             cfg.cls += ' navbar-nav'
3079         }
3080         
3081         if (this.parent().sidebar === true) {
3082             cfg = {
3083                 tag: 'ul',
3084                 cls: 'dashboard-menu'
3085             }
3086             
3087             return cfg;
3088         }
3089         
3090         if (this.form === true) {
3091             cfg = {
3092                 tag: 'form',
3093                 cls: 'navbar-form'
3094             }
3095             
3096             if (this.align === 'right') {
3097                 cfg.cls += ' navbar-right';
3098             } else {
3099                 cfg.cls += ' navbar-left';
3100             }
3101         }
3102         
3103         if (this.align === 'right') {
3104             cfg.cls += ' navbar-right';
3105         }
3106         
3107         if (this.inverse) {
3108             cfg.cls += ' navbar-inverse';
3109             
3110         }
3111         
3112         
3113         return cfg;
3114     },
3115     
3116     setActiveItem : function(item)
3117     {
3118         var prev = false;
3119         Roo.each(this.navItems, function(v){
3120             if (v == item) {
3121                 return ;
3122             }
3123             if (v.isActive()) {
3124                 v.setActive(false, true);
3125                 prev = v;
3126                 
3127             }
3128             
3129         });
3130
3131         item.setActive(true, true);
3132         this.fireEvent('changed', this, item, prev);
3133         
3134         
3135     },
3136     
3137     
3138     register : function(item)
3139     {
3140         this.navItems.push( item);
3141         item.navId = this.navId;
3142     
3143     },
3144     getNavItem: function(tabId)
3145     {
3146         var ret = false;
3147         Roo.each(this.navItems, function(e) {
3148             if (e.tabId == tabId) {
3149                ret =  e;
3150                return false;
3151             }
3152             return true;
3153             
3154         });
3155         return ret;
3156     }
3157 });
3158
3159  
3160 Roo.apply(Roo.bootstrap.NavGroup, {
3161     
3162     groups: {},
3163     
3164     register : function(navgrp)
3165     {
3166         this.groups[navgrp.navId] = navgrp;
3167         
3168     },
3169     get: function(navId) {
3170         return this.groups[navId];
3171     }
3172     
3173     
3174     
3175 });
3176
3177  /*
3178  * - LGPL
3179  *
3180  * row
3181  * 
3182  */
3183
3184 /**
3185  * @class Roo.bootstrap.Navbar.Item
3186  * @extends Roo.bootstrap.Component
3187  * Bootstrap Navbar.Button class
3188  * @cfg {String} href  link to
3189  * @cfg {String} html content of button
3190  * @cfg {String} badge text inside badge
3191  * @cfg {String} glyphicon name of glyphicon
3192  * @cfg {String} icon name of font awesome icon
3193  * @cfg {Boolean} active Is item active
3194  * @cfg {Boolean} preventDefault (true | false) default false
3195  * @cfg {String} tabId the tab that this item activates.
3196   
3197  * @constructor
3198  * Create a new Navbar Button
3199  * @param {Object} config The config object
3200  */
3201 Roo.bootstrap.Navbar.Item = function(config){
3202     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3203     this.addEvents({
3204         // raw events
3205         /**
3206          * @event click
3207          * The raw click event for the entire grid.
3208          * @param {Roo.EventObject} e
3209          */
3210         "click" : true,
3211          /**
3212             * @event changed
3213             * Fires when the active item active state changes
3214             * @param {Roo.bootstrap.Navbar.Item} this
3215             * @param {boolean} state the new state
3216              
3217          */
3218         'changed': true
3219     });
3220    
3221 };
3222
3223 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3224     
3225     href: false,
3226     html: '',
3227     badge: '',
3228     icon: false,
3229     glyphicon: false,
3230     active: false,
3231     preventDefault : false,
3232     tabId : false,
3233     
3234     getAutoCreate : function(){
3235         
3236         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3237         
3238         if (this.parent().parent().sidebar === true) {
3239             cfg = {
3240                 tag: 'li',
3241                 cls: '',
3242                 cn: [
3243                     {
3244                     tag: 'p',
3245                     cls: ''
3246                     }
3247                 ]
3248             }
3249             
3250             if (this.html) {
3251                 cfg.cn[0].html = this.html;
3252             }
3253             
3254             if (this.active) {
3255                 this.cls += ' active';
3256             }
3257             
3258             if (this.menu) {
3259                 cfg.cn[0].cls += ' dropdown-toggle';
3260                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3261             }
3262             
3263             if (this.href) {
3264                 cfg.cn[0].tag = 'a',
3265                 cfg.cn[0].href = this.href;
3266             }
3267             
3268             if (this.glyphicon) {
3269                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3270             }
3271                 
3272             if (this.icon) {
3273                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3274             }
3275             
3276             return cfg;
3277         }
3278         
3279         cfg = {
3280             tag: 'li',
3281                 cls: 'nav-item'
3282         }
3283             
3284         if (this.active) {
3285             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3286         }
3287             
3288         cfg.cn = [
3289             {
3290                 tag: 'p',
3291                 html: 'Text'
3292             }
3293         ];
3294         
3295         if (this.glyphicon) {
3296             if(cfg.html){cfg.html = ' ' + this.html};
3297             cfg.cn=[
3298                 {
3299                     tag: 'span',
3300                     cls: 'glyphicon glyphicon-' + this.glyphicon
3301                 }
3302             ];
3303         }
3304         
3305         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3306         
3307         if (this.menu) {
3308             cfg.cn[0].tag='a';
3309             cfg.cn[0].href='#';
3310             cfg.cn[0].html += " <span class='caret'></span>";
3311         //}else if (!this.href) {
3312         //    cfg.cn[0].tag='p';
3313         //    cfg.cn[0].cls='navbar-text';
3314         } else {
3315             cfg.cn[0].tag='a';
3316             cfg.cn[0].href=this.href||'#';
3317             cfg.cn[0].html=this.html;
3318         }
3319         
3320         if (this.badge !== '') {
3321             
3322             cfg.cn[0].cn=[
3323             cfg.cn[0].html + ' ',
3324             {
3325                 tag: 'span',
3326                 cls: 'badge',
3327                 html: this.badge
3328             }
3329             ];
3330             cfg.cn[0].html=''
3331         }
3332          
3333         if (this.icon) {
3334             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3335         }
3336         
3337         return cfg;
3338     },
3339     initEvents: function() {
3340        // Roo.log('init events?');
3341        // Roo.log(this.el.dom);
3342         this.el.select('a',true).on('click', this.onClick, this);
3343         // at this point parent should be available..
3344         this.parent().register(this);
3345     },
3346     
3347     onClick : function(e)
3348     {
3349         if(this.preventDefault){
3350             e.preventDefault();
3351         }
3352         
3353         if(this.fireEvent('click', this, e) === false){
3354             return;
3355         };
3356         
3357         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3358              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3359                 this.parent().setActiveItem(this);
3360             }
3361             
3362             
3363             
3364         } 
3365     },
3366     
3367     isActive: function () {
3368         return this.active
3369     },
3370     setActive : function(state, fire)
3371     {
3372         this.active = state;
3373         if (!state ) {
3374             this.el.removeClass('active');
3375         } else if (!this.el.hasClass('active')) {
3376             this.el.addClass('active');
3377         }
3378         if (fire) {
3379             this.fireEvent('changed', this, state);
3380         }
3381         
3382         
3383     }
3384      // this should not be here...
3385  
3386 });
3387  
3388
3389  /*
3390  * - LGPL
3391  *
3392  * row
3393  * 
3394  */
3395
3396 /**
3397  * @class Roo.bootstrap.Row
3398  * @extends Roo.bootstrap.Component
3399  * Bootstrap Row class (contains columns...)
3400  * 
3401  * @constructor
3402  * Create a new Row
3403  * @param {Object} config The config object
3404  */
3405
3406 Roo.bootstrap.Row = function(config){
3407     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3408 };
3409
3410 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3411     
3412     getAutoCreate : function(){
3413        return {
3414             cls: 'row clearfix'
3415        };
3416     }
3417     
3418     
3419 });
3420
3421  
3422
3423  /*
3424  * - LGPL
3425  *
3426  * element
3427  * 
3428  */
3429
3430 /**
3431  * @class Roo.bootstrap.Element
3432  * @extends Roo.bootstrap.Component
3433  * Bootstrap Element class
3434  * @cfg {String} html contents of the element
3435  * @cfg {String} tag tag of the element
3436  * @cfg {String} cls class of the element
3437  * 
3438  * @constructor
3439  * Create a new Element
3440  * @param {Object} config The config object
3441  */
3442
3443 Roo.bootstrap.Element = function(config){
3444     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3445 };
3446
3447 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3448     
3449     tag: 'div',
3450     cls: '',
3451     html: '',
3452      
3453     
3454     getAutoCreate : function(){
3455         
3456         var cfg = {
3457             tag: this.tag,
3458             cls: this.cls,
3459             html: this.html
3460         }
3461         
3462         
3463         
3464         return cfg;
3465     }
3466    
3467 });
3468
3469  
3470
3471  /*
3472  * - LGPL
3473  *
3474  * pagination
3475  * 
3476  */
3477
3478 /**
3479  * @class Roo.bootstrap.Pagination
3480  * @extends Roo.bootstrap.Component
3481  * Bootstrap Pagination class
3482  * @cfg {String} size xs | sm | md | lg
3483  * @cfg {Boolean} inverse false | true
3484  * 
3485  * @constructor
3486  * Create a new Pagination
3487  * @param {Object} config The config object
3488  */
3489
3490 Roo.bootstrap.Pagination = function(config){
3491     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3492 };
3493
3494 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3495     
3496     cls: false,
3497     size: false,
3498     inverse: false,
3499     
3500     getAutoCreate : function(){
3501         var cfg = {
3502             tag: 'ul',
3503                 cls: 'pagination'
3504         };
3505         if (this.inverse) {
3506             cfg.cls += ' inverse';
3507         }
3508         if (this.html) {
3509             cfg.html=this.html;
3510         }
3511         if (this.cls) {
3512             cfg.cls += " " + this.cls;
3513         }
3514         return cfg;
3515     }
3516    
3517 });
3518
3519  
3520
3521  /*
3522  * - LGPL
3523  *
3524  * Pagination item
3525  * 
3526  */
3527
3528
3529 /**
3530  * @class Roo.bootstrap.PaginationItem
3531  * @extends Roo.bootstrap.Component
3532  * Bootstrap PaginationItem class
3533  * @cfg {String} html text
3534  * @cfg {String} href the link
3535  * @cfg {Boolean} preventDefault (true | false) default true
3536  * @cfg {Boolean} active (true | false) default false
3537  * 
3538  * 
3539  * @constructor
3540  * Create a new PaginationItem
3541  * @param {Object} config The config object
3542  */
3543
3544
3545 Roo.bootstrap.PaginationItem = function(config){
3546     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3547     this.addEvents({
3548         // raw events
3549         /**
3550          * @event click
3551          * The raw click event for the entire grid.
3552          * @param {Roo.EventObject} e
3553          */
3554         "click" : true
3555     });
3556 };
3557
3558 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3559     
3560     href : false,
3561     html : false,
3562     preventDefault: true,
3563     active : false,
3564     cls : false,
3565     
3566     getAutoCreate : function(){
3567         var cfg= {
3568             tag: 'li',
3569             cn: [
3570                 {
3571                     tag : 'a',
3572                     href : this.href ? this.href : '#',
3573                     html : this.html ? this.html : ''
3574                 }
3575             ]
3576         };
3577         
3578         if(this.cls){
3579             cfg.cls = this.cls;
3580         }
3581         
3582         if(this.active){
3583             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3584         }
3585         
3586         return cfg;
3587     },
3588     
3589     initEvents: function() {
3590         
3591         this.el.on('click', this.onClick, this);
3592         
3593     },
3594     onClick : function(e)
3595     {
3596         Roo.log('PaginationItem on click ');
3597         if(this.preventDefault){
3598             e.preventDefault();
3599         }
3600         
3601         this.fireEvent('click', this, e);
3602     }
3603    
3604 });
3605
3606  
3607
3608  /*
3609  * - LGPL
3610  *
3611  * slider
3612  * 
3613  */
3614
3615
3616 /**
3617  * @class Roo.bootstrap.Slider
3618  * @extends Roo.bootstrap.Component
3619  * Bootstrap Slider class
3620  *    
3621  * @constructor
3622  * Create a new Slider
3623  * @param {Object} config The config object
3624  */
3625
3626 Roo.bootstrap.Slider = function(config){
3627     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3628 };
3629
3630 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3631     
3632     getAutoCreate : function(){
3633         
3634         var cfg = {
3635             tag: 'div',
3636             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3637             cn: [
3638                 {
3639                     tag: 'a',
3640                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3641                 }
3642             ]
3643         }
3644         
3645         return cfg;
3646     }
3647    
3648 });
3649
3650  /*
3651  * - LGPL
3652  *
3653  * table
3654  * 
3655  */
3656
3657 /**
3658  * @class Roo.bootstrap.Table
3659  * @extends Roo.bootstrap.Component
3660  * Bootstrap Table class
3661  * @cfg {String} cls table class
3662  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3663  * @cfg {String} bgcolor Specifies the background color for a table
3664  * @cfg {Number} border Specifies whether the table cells should have borders or not
3665  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3666  * @cfg {Number} cellspacing Specifies the space between cells
3667  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3668  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3669  * @cfg {String} sortable Specifies that the table should be sortable
3670  * @cfg {String} summary Specifies a summary of the content of a table
3671  * @cfg {Number} width Specifies the width of a table
3672  * 
3673  * @cfg {boolean} striped Should the rows be alternative striped
3674  * @cfg {boolean} bordered Add borders to the table
3675  * @cfg {boolean} hover Add hover highlighting
3676  * @cfg {boolean} condensed Format condensed
3677  * @cfg {boolean} responsive Format condensed
3678  *
3679  
3680  
3681  * 
3682  * @constructor
3683  * Create a new Table
3684  * @param {Object} config The config object
3685  */
3686
3687 Roo.bootstrap.Table = function(config){
3688     Roo.bootstrap.Table.superclass.constructor.call(this, config);
3689     
3690     if (this.sm) {
3691         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3692         this.sm = this.selModel;
3693         this.sm.xmodule = this.xmodule || false;
3694     }
3695     if (this.cm && typeof(this.cm.config) == 'undefined') {
3696         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
3697         this.cm = this.colModel;
3698         this.cm.xmodule = this.xmodule || false;
3699     }
3700     if (this.store) {
3701         this.store= Roo.factory(this.store, Roo.data);
3702         this.ds = this.store;
3703         this.ds.xmodule = this.xmodule || false;
3704          
3705     }
3706 };
3707
3708 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
3709     
3710     cls: false,
3711     align: false,
3712     bgcolor: false,
3713     border: false,
3714     cellpadding: false,
3715     cellspacing: false,
3716     frame: false,
3717     rules: false,
3718     sortable: false,
3719     summary: false,
3720     width: false,
3721     striped : false,
3722     bordered: false,
3723     hover:  false,
3724     condensed : false,
3725     responsive : false,
3726     sm : false,
3727     cm : false,
3728     store : false,
3729     
3730     getAutoCreate : function(){
3731         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3732         
3733         cfg = {
3734             tag: 'table',
3735             cls : 'table',
3736             cn : []
3737         }
3738             
3739         if (this.striped) {
3740             cfg.cls += ' table-striped';
3741         }
3742         if (this.hover) {
3743             cfg.cls += ' table-hover';
3744         }
3745         if (this.bordered) {
3746             cfg.cls += ' table-bordered';
3747         }
3748         if (this.condensed) {
3749             cfg.cls += ' table-condensed';
3750         }
3751         if (this.responsive) {
3752             cfg.cls += ' table-responsive';
3753         }
3754         
3755           
3756         
3757         
3758         if (this.cls) {
3759             cfg.cls+=  ' ' +this.cls;
3760         }
3761         
3762         // this lot should be simplifed...
3763         
3764         if (this.align) {
3765             cfg.align=this.align;
3766         }
3767         if (this.bgcolor) {
3768             cfg.bgcolor=this.bgcolor;
3769         }
3770         if (this.border) {
3771             cfg.border=this.border;
3772         }
3773         if (this.cellpadding) {
3774             cfg.cellpadding=this.cellpadding;
3775         }
3776         if (this.cellspacing) {
3777             cfg.cellspacing=this.cellspacing;
3778         }
3779         if (this.frame) {
3780             cfg.frame=this.frame;
3781         }
3782         if (this.rules) {
3783             cfg.rules=this.rules;
3784         }
3785         if (this.sortable) {
3786             cfg.sortable=this.sortable;
3787         }
3788         if (this.summary) {
3789             cfg.summary=this.summary;
3790         }
3791         if (this.width) {
3792             cfg.width=this.width;
3793         }
3794         
3795         if(this.store || this.cm){
3796             cfg.cn.push(this.renderHeader());
3797             cfg.cn.push(this.renderBody());
3798             cfg.cn.push(this.renderFooter());
3799             
3800             cfg.cls+=  ' TableGrid';
3801         }
3802         
3803         return cfg;
3804     },
3805 //    
3806 //    initTableGrid : function()
3807 //    {
3808 //        var cfg = {};
3809 //        
3810 //        var header = {
3811 //            tag: 'thead',
3812 //            cn : []
3813 //        };
3814 //        
3815 //        var cm = this.cm;
3816 //        
3817 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3818 //            header.cn.push({
3819 //                tag: 'th',
3820 //                html: cm.getColumnHeader(i)
3821 //            })
3822 //        }
3823 //        
3824 //        cfg.push(header);
3825 //        
3826 //        return cfg;
3827 //        
3828 //        
3829 //    },
3830     
3831     initEvents : function()
3832     {   
3833         if(!this.store || !this.cm){
3834             return;
3835         }
3836         
3837         Roo.log('initEvents with ds!!!!');
3838         
3839         var _this = this;
3840         
3841         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3842             e.on('click', _this.sort, _this);
3843         });
3844 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3845 //        this.maskEl.enableDisplayMode("block");
3846 //        this.maskEl.show();
3847         
3848         this.store.on('load', this.onLoad, this);
3849         this.store.on('beforeload', this.onBeforeLoad, this);
3850         
3851         this.store.load();
3852         
3853         
3854         
3855     },
3856     
3857     sort : function(e,el)
3858     {
3859         var col = Roo.get(el)
3860         
3861         if(!col.hasClass('sortable')){
3862             return;
3863         }
3864         
3865         var sort = col.attr('sort');
3866         var dir = 'ASC';
3867         
3868         if(col.hasClass('glyphicon-arrow-up')){
3869             dir = 'DESC';
3870         }
3871         
3872         this.store.sortInfo = {field : sort, direction : dir};
3873         
3874         this.store.load();
3875     },
3876     
3877     renderHeader : function()
3878     {
3879         var header = {
3880             tag: 'thead',
3881             cn : []
3882         };
3883         
3884         var cm = this.cm;
3885         
3886         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3887             
3888             var config = cm.config[i];
3889             
3890             var c = {
3891                 tag: 'th',
3892                 html: cm.getColumnHeader(i)
3893             };
3894             
3895             if(typeof(config.dataIndex) != 'undefined'){
3896                 c.sort = config.dataIndex;
3897             }
3898             
3899             if(typeof(config.sortable) != 'undefined' && config.sortable){
3900                 c.cls = 'sortable';
3901             }
3902             
3903             if(typeof(config.width) != 'undefined'){
3904                 c.style = 'width:' + config.width + 'px';
3905             }
3906             
3907             header.cn.push(c)
3908         }
3909         
3910         return header;
3911     },
3912     
3913     renderBody : function()
3914     {
3915         var body = {
3916             tag: 'tbody',
3917             cn : []
3918         };
3919         
3920         return body;
3921     },
3922     
3923     renderFooter : function()
3924     {
3925         var footer = {
3926             tag: 'tfoot',
3927             cn : []
3928         };
3929         
3930         return footer;
3931     },
3932     
3933     onLoad : function()
3934     {
3935         Roo.log('ds onload');
3936         
3937         var _this = this;
3938         var cm = this.cm;
3939         
3940         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3941             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3942             
3943             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3944                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3945             }
3946             
3947             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3948                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3949             }
3950         });
3951         
3952         var tbody = this.el.select('tbody', true).first();
3953         
3954         var renders = [];
3955         
3956         if(this.store.getCount() > 0){
3957             this.store.data.each(function(d){
3958                 var row = {
3959                     tag : 'tr',
3960                     cn : []
3961                 };
3962                 
3963                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3964                     var renderer = cm.getRenderer(i);
3965                     var config = cm.config[i];
3966                     var value = '';
3967                     var id = Roo.id();
3968                     
3969                     if(typeof(renderer) !== 'undefined'){
3970                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3971                     }
3972                     
3973                     if(typeof(value) === 'object'){
3974                         renders.push({
3975                             id : id,
3976                             cfg : value 
3977                         })
3978                     }
3979                     
3980                     var td = {
3981                         tag: 'td',
3982                         id: id,
3983                         html: (typeof(value) === 'object') ? '' : value
3984                     };
3985                     
3986                     if(typeof(config.width) != 'undefined'){
3987                         td.style = 'width:' +  config.width + 'px';
3988                     }
3989                     
3990                     row.cn.push(td);
3991                    
3992                 }
3993                 
3994                 tbody.createChild(row);
3995                 
3996             });
3997         }
3998         
3999         
4000         if(renders.length){
4001             var _this = this;
4002             Roo.each(renders, function(r){
4003                 _this.renderColumn(r);
4004             })
4005         }
4006 //        
4007 //        if(this.loadMask){
4008 //            this.maskEl.hide();
4009 //        }
4010     },
4011     
4012     onBeforeLoad : function()
4013     {
4014         Roo.log('ds onBeforeLoad');
4015         
4016         this.clear();
4017         
4018 //        if(this.loadMask){
4019 //            this.maskEl.show();
4020 //        }
4021     },
4022     
4023     clear : function()
4024     {
4025         this.el.select('tbody', true).first().dom.innerHTML = '';
4026     },
4027     
4028     getSelectionModel : function(){
4029         if(!this.selModel){
4030             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4031         }
4032         return this.selModel;
4033     },
4034     
4035     renderColumn : function(r)
4036     {
4037         var _this = this;
4038         r.cfg.render(Roo.get(r.id));
4039         
4040         if(r.cfg.cn){
4041             Roo.each(r.cfg.cn, function(c){
4042                 var child = {
4043                     id: r.id,
4044                     cfg: c
4045                 }
4046                 _this.renderColumn(child);
4047             })
4048         }
4049     }
4050    
4051 });
4052
4053  
4054
4055  /*
4056  * - LGPL
4057  *
4058  * table cell
4059  * 
4060  */
4061
4062 /**
4063  * @class Roo.bootstrap.TableCell
4064  * @extends Roo.bootstrap.Component
4065  * Bootstrap TableCell class
4066  * @cfg {String} html cell contain text
4067  * @cfg {String} cls cell class
4068  * @cfg {String} tag cell tag (td|th) default td
4069  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4070  * @cfg {String} align Aligns the content in a cell
4071  * @cfg {String} axis Categorizes cells
4072  * @cfg {String} bgcolor Specifies the background color of a cell
4073  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4074  * @cfg {Number} colspan Specifies the number of columns a cell should span
4075  * @cfg {String} headers Specifies one or more header cells a cell is related to
4076  * @cfg {Number} height Sets the height of a cell
4077  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4078  * @cfg {Number} rowspan Sets the number of rows a cell should span
4079  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4080  * @cfg {String} valign Vertical aligns the content in a cell
4081  * @cfg {Number} width Specifies the width of a cell
4082  * 
4083  * @constructor
4084  * Create a new TableCell
4085  * @param {Object} config The config object
4086  */
4087
4088 Roo.bootstrap.TableCell = function(config){
4089     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4090 };
4091
4092 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4093     
4094     html: false,
4095     cls: false,
4096     tag: false,
4097     abbr: false,
4098     align: false,
4099     axis: false,
4100     bgcolor: false,
4101     charoff: false,
4102     colspan: false,
4103     headers: false,
4104     height: false,
4105     nowrap: false,
4106     rowspan: false,
4107     scope: false,
4108     valign: false,
4109     width: false,
4110     
4111     
4112     getAutoCreate : function(){
4113         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4114         
4115         cfg = {
4116             tag: 'td'
4117         }
4118         
4119         if(this.tag){
4120             cfg.tag = this.tag;
4121         }
4122         
4123         if (this.html) {
4124             cfg.html=this.html
4125         }
4126         if (this.cls) {
4127             cfg.cls=this.cls
4128         }
4129         if (this.abbr) {
4130             cfg.abbr=this.abbr
4131         }
4132         if (this.align) {
4133             cfg.align=this.align
4134         }
4135         if (this.axis) {
4136             cfg.axis=this.axis
4137         }
4138         if (this.bgcolor) {
4139             cfg.bgcolor=this.bgcolor
4140         }
4141         if (this.charoff) {
4142             cfg.charoff=this.charoff
4143         }
4144         if (this.colspan) {
4145             cfg.colspan=this.colspan
4146         }
4147         if (this.headers) {
4148             cfg.headers=this.headers
4149         }
4150         if (this.height) {
4151             cfg.height=this.height
4152         }
4153         if (this.nowrap) {
4154             cfg.nowrap=this.nowrap
4155         }
4156         if (this.rowspan) {
4157             cfg.rowspan=this.rowspan
4158         }
4159         if (this.scope) {
4160             cfg.scope=this.scope
4161         }
4162         if (this.valign) {
4163             cfg.valign=this.valign
4164         }
4165         if (this.width) {
4166             cfg.width=this.width
4167         }
4168         
4169         
4170         return cfg;
4171     }
4172    
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * table row
4181  * 
4182  */
4183
4184 /**
4185  * @class Roo.bootstrap.TableRow
4186  * @extends Roo.bootstrap.Component
4187  * Bootstrap TableRow class
4188  * @cfg {String} cls row class
4189  * @cfg {String} align Aligns the content in a table row
4190  * @cfg {String} bgcolor Specifies a background color for a table row
4191  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4192  * @cfg {String} valign Vertical aligns the content in a table row
4193  * 
4194  * @constructor
4195  * Create a new TableRow
4196  * @param {Object} config The config object
4197  */
4198
4199 Roo.bootstrap.TableRow = function(config){
4200     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4201 };
4202
4203 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4204     
4205     cls: false,
4206     align: false,
4207     bgcolor: false,
4208     charoff: false,
4209     valign: false,
4210     
4211     getAutoCreate : function(){
4212         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4213         
4214         cfg = {
4215             tag: 'tr'
4216         }
4217             
4218         if(this.cls){
4219             cfg.cls = this.cls;
4220         }
4221         if(this.align){
4222             cfg.align = this.align;
4223         }
4224         if(this.bgcolor){
4225             cfg.bgcolor = this.bgcolor;
4226         }
4227         if(this.charoff){
4228             cfg.charoff = this.charoff;
4229         }
4230         if(this.valign){
4231             cfg.valign = this.valign;
4232         }
4233         
4234         return cfg;
4235     }
4236    
4237 });
4238
4239  
4240
4241  /*
4242  * - LGPL
4243  *
4244  * table body
4245  * 
4246  */
4247
4248 /**
4249  * @class Roo.bootstrap.TableBody
4250  * @extends Roo.bootstrap.Component
4251  * Bootstrap TableBody class
4252  * @cfg {String} cls element class
4253  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4254  * @cfg {String} align Aligns the content inside the element
4255  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4256  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4257  * 
4258  * @constructor
4259  * Create a new TableBody
4260  * @param {Object} config The config object
4261  */
4262
4263 Roo.bootstrap.TableBody = function(config){
4264     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4265 };
4266
4267 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4268     
4269     cls: false,
4270     tag: false,
4271     align: false,
4272     charoff: false,
4273     valign: false,
4274     
4275     getAutoCreate : function(){
4276         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4277         
4278         cfg = {
4279             tag: 'tbody'
4280         }
4281             
4282         if (this.cls) {
4283             cfg.cls=this.cls
4284         }
4285         if(this.tag){
4286             cfg.tag = this.tag;
4287         }
4288         
4289         if(this.align){
4290             cfg.align = this.align;
4291         }
4292         if(this.charoff){
4293             cfg.charoff = this.charoff;
4294         }
4295         if(this.valign){
4296             cfg.valign = this.valign;
4297         }
4298         
4299         return cfg;
4300     }
4301     
4302     
4303 //    initEvents : function()
4304 //    {
4305 //        
4306 //        if(!this.store){
4307 //            return;
4308 //        }
4309 //        
4310 //        this.store = Roo.factory(this.store, Roo.data);
4311 //        this.store.on('load', this.onLoad, this);
4312 //        
4313 //        this.store.load();
4314 //        
4315 //    },
4316 //    
4317 //    onLoad: function () 
4318 //    {   
4319 //        this.fireEvent('load', this);
4320 //    }
4321 //    
4322 //   
4323 });
4324
4325  
4326
4327  /*
4328  * Based on:
4329  * Ext JS Library 1.1.1
4330  * Copyright(c) 2006-2007, Ext JS, LLC.
4331  *
4332  * Originally Released Under LGPL - original licence link has changed is not relivant.
4333  *
4334  * Fork - LGPL
4335  * <script type="text/javascript">
4336  */
4337
4338 // as we use this in bootstrap.
4339 Roo.namespace('Roo.form');
4340  /**
4341  * @class Roo.form.Action
4342  * Internal Class used to handle form actions
4343  * @constructor
4344  * @param {Roo.form.BasicForm} el The form element or its id
4345  * @param {Object} config Configuration options
4346  */
4347
4348  
4349  
4350 // define the action interface
4351 Roo.form.Action = function(form, options){
4352     this.form = form;
4353     this.options = options || {};
4354 };
4355 /**
4356  * Client Validation Failed
4357  * @const 
4358  */
4359 Roo.form.Action.CLIENT_INVALID = 'client';
4360 /**
4361  * Server Validation Failed
4362  * @const 
4363  */
4364 Roo.form.Action.SERVER_INVALID = 'server';
4365  /**
4366  * Connect to Server Failed
4367  * @const 
4368  */
4369 Roo.form.Action.CONNECT_FAILURE = 'connect';
4370 /**
4371  * Reading Data from Server Failed
4372  * @const 
4373  */
4374 Roo.form.Action.LOAD_FAILURE = 'load';
4375
4376 Roo.form.Action.prototype = {
4377     type : 'default',
4378     failureType : undefined,
4379     response : undefined,
4380     result : undefined,
4381
4382     // interface method
4383     run : function(options){
4384
4385     },
4386
4387     // interface method
4388     success : function(response){
4389
4390     },
4391
4392     // interface method
4393     handleResponse : function(response){
4394
4395     },
4396
4397     // default connection failure
4398     failure : function(response){
4399         
4400         this.response = response;
4401         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4402         this.form.afterAction(this, false);
4403     },
4404
4405     processResponse : function(response){
4406         this.response = response;
4407         if(!response.responseText){
4408             return true;
4409         }
4410         this.result = this.handleResponse(response);
4411         return this.result;
4412     },
4413
4414     // utility functions used internally
4415     getUrl : function(appendParams){
4416         var url = this.options.url || this.form.url || this.form.el.dom.action;
4417         if(appendParams){
4418             var p = this.getParams();
4419             if(p){
4420                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4421             }
4422         }
4423         return url;
4424     },
4425
4426     getMethod : function(){
4427         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4428     },
4429
4430     getParams : function(){
4431         var bp = this.form.baseParams;
4432         var p = this.options.params;
4433         if(p){
4434             if(typeof p == "object"){
4435                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4436             }else if(typeof p == 'string' && bp){
4437                 p += '&' + Roo.urlEncode(bp);
4438             }
4439         }else if(bp){
4440             p = Roo.urlEncode(bp);
4441         }
4442         return p;
4443     },
4444
4445     createCallback : function(){
4446         return {
4447             success: this.success,
4448             failure: this.failure,
4449             scope: this,
4450             timeout: (this.form.timeout*1000),
4451             upload: this.form.fileUpload ? this.success : undefined
4452         };
4453     }
4454 };
4455
4456 Roo.form.Action.Submit = function(form, options){
4457     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4458 };
4459
4460 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4461     type : 'submit',
4462
4463     haveProgress : false,
4464     uploadComplete : false,
4465     
4466     // uploadProgress indicator.
4467     uploadProgress : function()
4468     {
4469         if (!this.form.progressUrl) {
4470             return;
4471         }
4472         
4473         if (!this.haveProgress) {
4474             Roo.MessageBox.progress("Uploading", "Uploading");
4475         }
4476         if (this.uploadComplete) {
4477            Roo.MessageBox.hide();
4478            return;
4479         }
4480         
4481         this.haveProgress = true;
4482    
4483         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4484         
4485         var c = new Roo.data.Connection();
4486         c.request({
4487             url : this.form.progressUrl,
4488             params: {
4489                 id : uid
4490             },
4491             method: 'GET',
4492             success : function(req){
4493                //console.log(data);
4494                 var rdata = false;
4495                 var edata;
4496                 try  {
4497                    rdata = Roo.decode(req.responseText)
4498                 } catch (e) {
4499                     Roo.log("Invalid data from server..");
4500                     Roo.log(edata);
4501                     return;
4502                 }
4503                 if (!rdata || !rdata.success) {
4504                     Roo.log(rdata);
4505                     Roo.MessageBox.alert(Roo.encode(rdata));
4506                     return;
4507                 }
4508                 var data = rdata.data;
4509                 
4510                 if (this.uploadComplete) {
4511                    Roo.MessageBox.hide();
4512                    return;
4513                 }
4514                    
4515                 if (data){
4516                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4517                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4518                     );
4519                 }
4520                 this.uploadProgress.defer(2000,this);
4521             },
4522        
4523             failure: function(data) {
4524                 Roo.log('progress url failed ');
4525                 Roo.log(data);
4526             },
4527             scope : this
4528         });
4529            
4530     },
4531     
4532     
4533     run : function()
4534     {
4535         // run get Values on the form, so it syncs any secondary forms.
4536         this.form.getValues();
4537         
4538         var o = this.options;
4539         var method = this.getMethod();
4540         var isPost = method == 'POST';
4541         if(o.clientValidation === false || this.form.isValid()){
4542             
4543             if (this.form.progressUrl) {
4544                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4545                     (new Date() * 1) + '' + Math.random());
4546                     
4547             } 
4548             
4549             
4550             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4551                 form:this.form.el.dom,
4552                 url:this.getUrl(!isPost),
4553                 method: method,
4554                 params:isPost ? this.getParams() : null,
4555                 isUpload: this.form.fileUpload
4556             }));
4557             
4558             this.uploadProgress();
4559
4560         }else if (o.clientValidation !== false){ // client validation failed
4561             this.failureType = Roo.form.Action.CLIENT_INVALID;
4562             this.form.afterAction(this, false);
4563         }
4564     },
4565
4566     success : function(response)
4567     {
4568         this.uploadComplete= true;
4569         if (this.haveProgress) {
4570             Roo.MessageBox.hide();
4571         }
4572         
4573         
4574         var result = this.processResponse(response);
4575         if(result === true || result.success){
4576             this.form.afterAction(this, true);
4577             return;
4578         }
4579         if(result.errors){
4580             this.form.markInvalid(result.errors);
4581             this.failureType = Roo.form.Action.SERVER_INVALID;
4582         }
4583         this.form.afterAction(this, false);
4584     },
4585     failure : function(response)
4586     {
4587         this.uploadComplete= true;
4588         if (this.haveProgress) {
4589             Roo.MessageBox.hide();
4590         }
4591         
4592         this.response = response;
4593         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4594         this.form.afterAction(this, false);
4595     },
4596     
4597     handleResponse : function(response){
4598         if(this.form.errorReader){
4599             var rs = this.form.errorReader.read(response);
4600             var errors = [];
4601             if(rs.records){
4602                 for(var i = 0, len = rs.records.length; i < len; i++) {
4603                     var r = rs.records[i];
4604                     errors[i] = r.data;
4605                 }
4606             }
4607             if(errors.length < 1){
4608                 errors = null;
4609             }
4610             return {
4611                 success : rs.success,
4612                 errors : errors
4613             };
4614         }
4615         var ret = false;
4616         try {
4617             ret = Roo.decode(response.responseText);
4618         } catch (e) {
4619             ret = {
4620                 success: false,
4621                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4622                 errors : []
4623             };
4624         }
4625         return ret;
4626         
4627     }
4628 });
4629
4630
4631 Roo.form.Action.Load = function(form, options){
4632     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4633     this.reader = this.form.reader;
4634 };
4635
4636 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4637     type : 'load',
4638
4639     run : function(){
4640         
4641         Roo.Ajax.request(Roo.apply(
4642                 this.createCallback(), {
4643                     method:this.getMethod(),
4644                     url:this.getUrl(false),
4645                     params:this.getParams()
4646         }));
4647     },
4648
4649     success : function(response){
4650         
4651         var result = this.processResponse(response);
4652         if(result === true || !result.success || !result.data){
4653             this.failureType = Roo.form.Action.LOAD_FAILURE;
4654             this.form.afterAction(this, false);
4655             return;
4656         }
4657         this.form.clearInvalid();
4658         this.form.setValues(result.data);
4659         this.form.afterAction(this, true);
4660     },
4661
4662     handleResponse : function(response){
4663         if(this.form.reader){
4664             var rs = this.form.reader.read(response);
4665             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4666             return {
4667                 success : rs.success,
4668                 data : data
4669             };
4670         }
4671         return Roo.decode(response.responseText);
4672     }
4673 });
4674
4675 Roo.form.Action.ACTION_TYPES = {
4676     'load' : Roo.form.Action.Load,
4677     'submit' : Roo.form.Action.Submit
4678 };/*
4679  * - LGPL
4680  *
4681  * form
4682  * 
4683  */
4684
4685 /**
4686  * @class Roo.bootstrap.Form
4687  * @extends Roo.bootstrap.Component
4688  * Bootstrap Form class
4689  * @cfg {String} method  GET | POST (default POST)
4690  * @cfg {String} labelAlign top | left (default top)
4691   * @cfg {String} align left  | right - for navbars
4692
4693  * 
4694  * @constructor
4695  * Create a new Form
4696  * @param {Object} config The config object
4697  */
4698
4699
4700 Roo.bootstrap.Form = function(config){
4701     Roo.bootstrap.Form.superclass.constructor.call(this, config);
4702     this.addEvents({
4703         /**
4704          * @event clientvalidation
4705          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4706          * @param {Form} this
4707          * @param {Boolean} valid true if the form has passed client-side validation
4708          */
4709         clientvalidation: true,
4710         /**
4711          * @event beforeaction
4712          * Fires before any action is performed. Return false to cancel the action.
4713          * @param {Form} this
4714          * @param {Action} action The action to be performed
4715          */
4716         beforeaction: true,
4717         /**
4718          * @event actionfailed
4719          * Fires when an action fails.
4720          * @param {Form} this
4721          * @param {Action} action The action that failed
4722          */
4723         actionfailed : true,
4724         /**
4725          * @event actioncomplete
4726          * Fires when an action is completed.
4727          * @param {Form} this
4728          * @param {Action} action The action that completed
4729          */
4730         actioncomplete : true
4731     });
4732     
4733 };
4734
4735 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
4736       
4737      /**
4738      * @cfg {String} method
4739      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4740      */
4741     method : 'POST',
4742     /**
4743      * @cfg {String} url
4744      * The URL to use for form actions if one isn't supplied in the action options.
4745      */
4746     /**
4747      * @cfg {Boolean} fileUpload
4748      * Set to true if this form is a file upload.
4749      */
4750      
4751     /**
4752      * @cfg {Object} baseParams
4753      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4754      */
4755       
4756     /**
4757      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4758      */
4759     timeout: 30,
4760     /**
4761      * @cfg {Sting} align (left|right) for navbar forms
4762      */
4763     align : 'left',
4764
4765     // private
4766     activeAction : null,
4767  
4768     /**
4769      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4770      * element by passing it or its id or mask the form itself by passing in true.
4771      * @type Mixed
4772      */
4773     waitMsgTarget : false,
4774     
4775      
4776     
4777     /**
4778      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4779      * element by passing it or its id or mask the form itself by passing in true.
4780      * @type Mixed
4781      */
4782     
4783     getAutoCreate : function(){
4784         
4785         var cfg = {
4786             tag: 'form',
4787             method : this.method || 'POST',
4788             id : this.id || Roo.id(),
4789             cls : ''
4790         }
4791         if (this.parent().xtype.match(/^Nav/)) {
4792             cfg.cls = 'navbar-form navbar-' + this.align;
4793             
4794         }
4795         
4796         if (this.labelAlign == 'left' ) {
4797             cfg.cls += ' form-horizontal';
4798         }
4799         
4800         
4801         return cfg;
4802     },
4803     initEvents : function()
4804     {
4805         this.el.on('submit', this.onSubmit, this);
4806         
4807         
4808     },
4809     // private
4810     onSubmit : function(e){
4811         e.stopEvent();
4812     },
4813     
4814      /**
4815      * Returns true if client-side validation on the form is successful.
4816      * @return Boolean
4817      */
4818     isValid : function(){
4819         var items = this.getItems();
4820         var valid = true;
4821         items.each(function(f){
4822            if(!f.validate()){
4823                valid = false;
4824                
4825            }
4826         });
4827         return valid;
4828     },
4829     /**
4830      * Returns true if any fields in this form have changed since their original load.
4831      * @return Boolean
4832      */
4833     isDirty : function(){
4834         var dirty = false;
4835         var items = this.getItems();
4836         items.each(function(f){
4837            if(f.isDirty()){
4838                dirty = true;
4839                return false;
4840            }
4841            return true;
4842         });
4843         return dirty;
4844     },
4845      /**
4846      * Performs a predefined action (submit or load) or custom actions you define on this form.
4847      * @param {String} actionName The name of the action type
4848      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
4849      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4850      * accept other config options):
4851      * <pre>
4852 Property          Type             Description
4853 ----------------  ---------------  ----------------------------------------------------------------------------------
4854 url               String           The url for the action (defaults to the form's url)
4855 method            String           The form method to use (defaults to the form's method, or POST if not defined)
4856 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
4857 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
4858                                    validate the form on the client (defaults to false)
4859      * </pre>
4860      * @return {BasicForm} this
4861      */
4862     doAction : function(action, options){
4863         if(typeof action == 'string'){
4864             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4865         }
4866         if(this.fireEvent('beforeaction', this, action) !== false){
4867             this.beforeAction(action);
4868             action.run.defer(100, action);
4869         }
4870         return this;
4871     },
4872     
4873     // private
4874     beforeAction : function(action){
4875         var o = action.options;
4876         
4877         // not really supported yet.. ??
4878         
4879         //if(this.waitMsgTarget === true){
4880             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4881         //}else if(this.waitMsgTarget){
4882         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4883         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4884         //}else {
4885         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4886        // }
4887          
4888     },
4889
4890     // private
4891     afterAction : function(action, success){
4892         this.activeAction = null;
4893         var o = action.options;
4894         
4895         //if(this.waitMsgTarget === true){
4896             this.el.unmask();
4897         //}else if(this.waitMsgTarget){
4898         //    this.waitMsgTarget.unmask();
4899         //}else{
4900         //    Roo.MessageBox.updateProgress(1);
4901         //    Roo.MessageBox.hide();
4902        // }
4903         // 
4904         if(success){
4905             if(o.reset){
4906                 this.reset();
4907             }
4908             Roo.callback(o.success, o.scope, [this, action]);
4909             this.fireEvent('actioncomplete', this, action);
4910             
4911         }else{
4912             
4913             // failure condition..
4914             // we have a scenario where updates need confirming.
4915             // eg. if a locking scenario exists..
4916             // we look for { errors : { needs_confirm : true }} in the response.
4917             if (
4918                 (typeof(action.result) != 'undefined')  &&
4919                 (typeof(action.result.errors) != 'undefined')  &&
4920                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4921            ){
4922                 var _t = this;
4923                 Roo.log("not supported yet");
4924                  /*
4925                 
4926                 Roo.MessageBox.confirm(
4927                     "Change requires confirmation",
4928                     action.result.errorMsg,
4929                     function(r) {
4930                         if (r != 'yes') {
4931                             return;
4932                         }
4933                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4934                     }
4935                     
4936                 );
4937                 */
4938                 
4939                 
4940                 return;
4941             }
4942             
4943             Roo.callback(o.failure, o.scope, [this, action]);
4944             // show an error message if no failed handler is set..
4945             if (!this.hasListener('actionfailed')) {
4946                 Roo.log("need to add dialog support");
4947                 /*
4948                 Roo.MessageBox.alert("Error",
4949                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4950                         action.result.errorMsg :
4951                         "Saving Failed, please check your entries or try again"
4952                 );
4953                 */
4954             }
4955             
4956             this.fireEvent('actionfailed', this, action);
4957         }
4958         
4959     },
4960     /**
4961      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4962      * @param {String} id The value to search for
4963      * @return Field
4964      */
4965     findField : function(id){
4966         var items = this.getItems();
4967         var field = items.get(id);
4968         if(!field){
4969              items.each(function(f){
4970                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4971                     field = f;
4972                     return false;
4973                 }
4974                 return true;
4975             });
4976         }
4977         return field || null;
4978     },
4979      /**
4980      * Mark fields in this form invalid in bulk.
4981      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4982      * @return {BasicForm} this
4983      */
4984     markInvalid : function(errors){
4985         if(errors instanceof Array){
4986             for(var i = 0, len = errors.length; i < len; i++){
4987                 var fieldError = errors[i];
4988                 var f = this.findField(fieldError.id);
4989                 if(f){
4990                     f.markInvalid(fieldError.msg);
4991                 }
4992             }
4993         }else{
4994             var field, id;
4995             for(id in errors){
4996                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4997                     field.markInvalid(errors[id]);
4998                 }
4999             }
5000         }
5001         //Roo.each(this.childForms || [], function (f) {
5002         //    f.markInvalid(errors);
5003         //});
5004         
5005         return this;
5006     },
5007
5008     /**
5009      * Set values for fields in this form in bulk.
5010      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5011      * @return {BasicForm} this
5012      */
5013     setValues : function(values){
5014         if(values instanceof Array){ // array of objects
5015             for(var i = 0, len = values.length; i < len; i++){
5016                 var v = values[i];
5017                 var f = this.findField(v.id);
5018                 if(f){
5019                     f.setValue(v.value);
5020                     if(this.trackResetOnLoad){
5021                         f.originalValue = f.getValue();
5022                     }
5023                 }
5024             }
5025         }else{ // object hash
5026             var field, id;
5027             for(id in values){
5028                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5029                     
5030                     if (field.setFromData && 
5031                         field.valueField && 
5032                         field.displayField &&
5033                         // combos' with local stores can 
5034                         // be queried via setValue()
5035                         // to set their value..
5036                         (field.store && !field.store.isLocal)
5037                         ) {
5038                         // it's a combo
5039                         var sd = { };
5040                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5041                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5042                         field.setFromData(sd);
5043                         
5044                     } else {
5045                         field.setValue(values[id]);
5046                     }
5047                     
5048                     
5049                     if(this.trackResetOnLoad){
5050                         field.originalValue = field.getValue();
5051                     }
5052                 }
5053             }
5054         }
5055          
5056         //Roo.each(this.childForms || [], function (f) {
5057         //    f.setValues(values);
5058         //});
5059                 
5060         return this;
5061     },
5062
5063     /**
5064      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5065      * they are returned as an array.
5066      * @param {Boolean} asString
5067      * @return {Object}
5068      */
5069     getValues : function(asString){
5070         //if (this.childForms) {
5071             // copy values from the child forms
5072         //    Roo.each(this.childForms, function (f) {
5073         //        this.setValues(f.getValues());
5074         //    }, this);
5075         //}
5076         
5077         
5078         
5079         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5080         if(asString === true){
5081             return fs;
5082         }
5083         return Roo.urlDecode(fs);
5084     },
5085     
5086     /**
5087      * Returns the fields in this form as an object with key/value pairs. 
5088      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5089      * @return {Object}
5090      */
5091     getFieldValues : function(with_hidden)
5092     {
5093         var items = this.getItems();
5094         var ret = {};
5095         items.each(function(f){
5096             if (!f.getName()) {
5097                 return;
5098             }
5099             var v = f.getValue();
5100             if (f.inputType =='radio') {
5101                 if (typeof(ret[f.getName()]) == 'undefined') {
5102                     ret[f.getName()] = ''; // empty..
5103                 }
5104                 
5105                 if (!f.el.dom.checked) {
5106                     return;
5107                     
5108                 }
5109                 v = f.el.dom.value;
5110                 
5111             }
5112             
5113             // not sure if this supported any more..
5114             if ((typeof(v) == 'object') && f.getRawValue) {
5115                 v = f.getRawValue() ; // dates..
5116             }
5117             // combo boxes where name != hiddenName...
5118             if (f.name != f.getName()) {
5119                 ret[f.name] = f.getRawValue();
5120             }
5121             ret[f.getName()] = v;
5122         });
5123         
5124         return ret;
5125     },
5126
5127     /**
5128      * Clears all invalid messages in this form.
5129      * @return {BasicForm} this
5130      */
5131     clearInvalid : function(){
5132         var items = this.getItems();
5133         
5134         items.each(function(f){
5135            f.clearInvalid();
5136         });
5137         
5138         
5139         
5140         return this;
5141     },
5142
5143     /**
5144      * Resets this form.
5145      * @return {BasicForm} this
5146      */
5147     reset : function(){
5148         var items = this.getItems();
5149         items.each(function(f){
5150             f.reset();
5151         });
5152         
5153         Roo.each(this.childForms || [], function (f) {
5154             f.reset();
5155         });
5156        
5157         
5158         return this;
5159     },
5160     getItems : function()
5161     {
5162         var r=new Roo.util.MixedCollection(false, function(o){
5163             return o.id || (o.id = Roo.id());
5164         });
5165         var iter = function(el) {
5166             if (el.inputEl) {
5167                 r.add(el);
5168             }
5169             if (!el.items) {
5170                 return;
5171             }
5172             Roo.each(el.items,function(e) {
5173                 iter(e);
5174             });
5175             
5176             
5177         };
5178         iter(this);
5179         return r;
5180         
5181         
5182         
5183         
5184     }
5185     
5186 });
5187
5188  
5189 /*
5190  * Based on:
5191  * Ext JS Library 1.1.1
5192  * Copyright(c) 2006-2007, Ext JS, LLC.
5193  *
5194  * Originally Released Under LGPL - original licence link has changed is not relivant.
5195  *
5196  * Fork - LGPL
5197  * <script type="text/javascript">
5198  */
5199 /**
5200  * @class Roo.form.VTypes
5201  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5202  * @singleton
5203  */
5204 Roo.form.VTypes = function(){
5205     // closure these in so they are only created once.
5206     var alpha = /^[a-zA-Z_]+$/;
5207     var alphanum = /^[a-zA-Z0-9_]+$/;
5208     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5209     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5210
5211     // All these messages and functions are configurable
5212     return {
5213         /**
5214          * The function used to validate email addresses
5215          * @param {String} value The email address
5216          */
5217         'email' : function(v){
5218             return email.test(v);
5219         },
5220         /**
5221          * The error text to display when the email validation function returns false
5222          * @type String
5223          */
5224         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5225         /**
5226          * The keystroke filter mask to be applied on email input
5227          * @type RegExp
5228          */
5229         'emailMask' : /[a-z0-9_\.\-@]/i,
5230
5231         /**
5232          * The function used to validate URLs
5233          * @param {String} value The URL
5234          */
5235         'url' : function(v){
5236             return url.test(v);
5237         },
5238         /**
5239          * The error text to display when the url validation function returns false
5240          * @type String
5241          */
5242         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5243         
5244         /**
5245          * The function used to validate alpha values
5246          * @param {String} value The value
5247          */
5248         'alpha' : function(v){
5249             return alpha.test(v);
5250         },
5251         /**
5252          * The error text to display when the alpha validation function returns false
5253          * @type String
5254          */
5255         'alphaText' : 'This field should only contain letters and _',
5256         /**
5257          * The keystroke filter mask to be applied on alpha input
5258          * @type RegExp
5259          */
5260         'alphaMask' : /[a-z_]/i,
5261
5262         /**
5263          * The function used to validate alphanumeric values
5264          * @param {String} value The value
5265          */
5266         'alphanum' : function(v){
5267             return alphanum.test(v);
5268         },
5269         /**
5270          * The error text to display when the alphanumeric validation function returns false
5271          * @type String
5272          */
5273         'alphanumText' : 'This field should only contain letters, numbers and _',
5274         /**
5275          * The keystroke filter mask to be applied on alphanumeric input
5276          * @type RegExp
5277          */
5278         'alphanumMask' : /[a-z0-9_]/i
5279     };
5280 }();/*
5281  * - LGPL
5282  *
5283  * Input
5284  * 
5285  */
5286
5287 /**
5288  * @class Roo.bootstrap.Input
5289  * @extends Roo.bootstrap.Component
5290  * Bootstrap Input class
5291  * @cfg {Boolean} disabled is it disabled
5292  * @cfg {String} fieldLabel - the label associated
5293  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5294  * @cfg {String} name name of the input
5295  * @cfg {string} fieldLabel - the label associated
5296  * @cfg {string}  inputType - input / file submit ...
5297  * @cfg {string} placeholder - placeholder to put in text.
5298  * @cfg {string}  before - input group add on before
5299  * @cfg {string} after - input group add on after
5300  * @cfg {string} size - (lg|sm) or leave empty..
5301  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5302  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5303  * @cfg {Number} md colspan out of 12 for computer-sized screens
5304  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5305  * @cfg {string} value default value of the input
5306  * @cfg {Number} labelWidth set the width of label (0-12)
5307  * @cfg {String} labelAlign (top|left)
5308  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5309  * 
5310  * 
5311  * @constructor
5312  * Create a new Input
5313  * @param {Object} config The config object
5314  */
5315
5316 Roo.bootstrap.Input = function(config){
5317     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5318    
5319         this.addEvents({
5320             /**
5321              * @event focus
5322              * Fires when this field receives input focus.
5323              * @param {Roo.form.Field} this
5324              */
5325             focus : true,
5326             /**
5327              * @event blur
5328              * Fires when this field loses input focus.
5329              * @param {Roo.form.Field} this
5330              */
5331             blur : true,
5332             /**
5333              * @event specialkey
5334              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5335              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5336              * @param {Roo.form.Field} this
5337              * @param {Roo.EventObject} e The event object
5338              */
5339             specialkey : true,
5340             /**
5341              * @event change
5342              * Fires just before the field blurs if the field value has changed.
5343              * @param {Roo.form.Field} this
5344              * @param {Mixed} newValue The new value
5345              * @param {Mixed} oldValue The original value
5346              */
5347             change : true,
5348             /**
5349              * @event invalid
5350              * Fires after the field has been marked as invalid.
5351              * @param {Roo.form.Field} this
5352              * @param {String} msg The validation message
5353              */
5354             invalid : true,
5355             /**
5356              * @event valid
5357              * Fires after the field has been validated with no errors.
5358              * @param {Roo.form.Field} this
5359              */
5360             valid : true,
5361              /**
5362              * @event keyup
5363              * Fires after the key up
5364              * @param {Roo.form.Field} this
5365              * @param {Roo.EventObject}  e The event Object
5366              */
5367             keyup : true
5368         });
5369 };
5370
5371 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5372      /**
5373      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5374       automatic validation (defaults to "keyup").
5375      */
5376     validationEvent : "keyup",
5377      /**
5378      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5379      */
5380     validateOnBlur : true,
5381     /**
5382      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5383      */
5384     validationDelay : 250,
5385      /**
5386      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5387      */
5388     focusClass : "x-form-focus",  // not needed???
5389     
5390        
5391     /**
5392      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5393      */
5394     invalidClass : "has-error",
5395     
5396     /**
5397      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5398      */
5399     selectOnFocus : false,
5400     
5401      /**
5402      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5403      */
5404     maskRe : null,
5405        /**
5406      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5407      */
5408     vtype : null,
5409     
5410       /**
5411      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5412      */
5413     disableKeyFilter : false,
5414     
5415        /**
5416      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5417      */
5418     disabled : false,
5419      /**
5420      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5421      */
5422     allowBlank : true,
5423     /**
5424      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5425      */
5426     blankText : "This field is required",
5427     
5428      /**
5429      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5430      */
5431     minLength : 0,
5432     /**
5433      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5434      */
5435     maxLength : Number.MAX_VALUE,
5436     /**
5437      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5438      */
5439     minLengthText : "The minimum length for this field is {0}",
5440     /**
5441      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5442      */
5443     maxLengthText : "The maximum length for this field is {0}",
5444   
5445     
5446     /**
5447      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5448      * If available, this function will be called only after the basic validators all return true, and will be passed the
5449      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5450      */
5451     validator : null,
5452     /**
5453      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5454      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5455      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5456      */
5457     regex : null,
5458     /**
5459      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5460      */
5461     regexText : "",
5462     
5463     
5464     
5465     fieldLabel : '',
5466     inputType : 'text',
5467     
5468     name : false,
5469     placeholder: false,
5470     before : false,
5471     after : false,
5472     size : false,
5473     // private
5474     hasFocus : false,
5475     preventMark: false,
5476     isFormField : true,
5477     value : '',
5478     labelWidth : 2,
5479     labelAlign : false,
5480     readOnly : false,
5481     
5482     parentLabelAlign : function()
5483     {
5484         var parent = this;
5485         while (parent.parent()) {
5486             parent = parent.parent();
5487             if (typeof(parent.labelAlign) !='undefined') {
5488                 return parent.labelAlign;
5489             }
5490         }
5491         return 'left';
5492         
5493     },
5494     
5495     getAutoCreate : function(){
5496         
5497         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5498         
5499         var id = Roo.id();
5500         
5501         var cfg = {};
5502         
5503         if(this.inputType != 'hidden'){
5504             cfg.cls = 'form-group' //input-group
5505         }
5506         
5507         var input =  {
5508             tag: 'input',
5509             id : id,
5510             type : this.inputType,
5511             value : this.value,
5512             cls : 'form-control',
5513             placeholder : this.placeholder || ''
5514             
5515         };
5516         
5517         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5518             input.maxLength = this.maxLength;
5519         }
5520         
5521         if (this.disabled) {
5522             input.disabled=true;
5523         }
5524         
5525         if (this.readOnly) {
5526             input.readonly=true;
5527         }
5528         
5529         if (this.name) {
5530             input.name = this.name;
5531         }
5532         if (this.size) {
5533             input.cls += ' input-' + this.size;
5534         }
5535         var settings=this;
5536         ['xs','sm','md','lg'].map(function(size){
5537             if (settings[size]) {
5538                 cfg.cls += ' col-' + size + '-' + settings[size];
5539             }
5540         });
5541         
5542         var inputblock = input;
5543         
5544         if (this.before || this.after) {
5545             
5546             inputblock = {
5547                 cls : 'input-group',
5548                 cn :  [] 
5549             };
5550             if (this.before) {
5551                 inputblock.cn.push({
5552                     tag :'span',
5553                     cls : 'input-group-addon',
5554                     html : this.before
5555                 });
5556             }
5557             inputblock.cn.push(input);
5558             if (this.after) {
5559                 inputblock.cn.push({
5560                     tag :'span',
5561                     cls : 'input-group-addon',
5562                     html : this.after
5563                 });
5564             }
5565             
5566         };
5567         
5568         if (align ==='left' && this.fieldLabel.length) {
5569                 Roo.log("left and has label");
5570                 cfg.cn = [
5571                     
5572                     {
5573                         tag: 'label',
5574                         'for' :  id,
5575                         cls : 'control-label col-sm-' + this.labelWidth,
5576                         html : this.fieldLabel
5577                         
5578                     },
5579                     {
5580                         cls : "col-sm-" + (12 - this.labelWidth), 
5581                         cn: [
5582                             inputblock
5583                         ]
5584                     }
5585                     
5586                 ];
5587         } else if ( this.fieldLabel.length) {
5588                 Roo.log(" label");
5589                  cfg.cn = [
5590                    
5591                     {
5592                         tag: 'label',
5593                         //cls : 'input-group-addon',
5594                         html : this.fieldLabel
5595                         
5596                     },
5597                     
5598                     inputblock
5599                     
5600                 ];
5601
5602         } else {
5603             
5604                 Roo.log(" no label && no align");
5605                 cfg.cn = [
5606                     
5607                         inputblock
5608                     
5609                 ];
5610                 
5611                 
5612         };
5613         Roo.log('input-parentType: ' + this.parentType);
5614         
5615         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5616            cfg.cls += ' navbar-form';
5617            Roo.log(cfg);
5618         }
5619         
5620         return cfg;
5621         
5622     },
5623     /**
5624      * return the real input element.
5625      */
5626     inputEl: function ()
5627     {
5628         return this.el.select('input.form-control',true).first();
5629     },
5630     setDisabled : function(v)
5631     {
5632         var i  = this.inputEl().dom;
5633         if (!v) {
5634             i.removeAttribute('disabled');
5635             return;
5636             
5637         }
5638         i.setAttribute('disabled','true');
5639     },
5640     initEvents : function()
5641     {
5642         
5643         this.inputEl().on("keydown" , this.fireKey,  this);
5644         this.inputEl().on("focus", this.onFocus,  this);
5645         this.inputEl().on("blur", this.onBlur,  this);
5646         
5647         this.inputEl().relayEvent('keyup', this);
5648
5649         // reference to original value for reset
5650         this.originalValue = this.getValue();
5651         //Roo.form.TextField.superclass.initEvents.call(this);
5652         if(this.validationEvent == 'keyup'){
5653             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5654             this.inputEl().on('keyup', this.filterValidation, this);
5655         }
5656         else if(this.validationEvent !== false){
5657             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5658         }
5659         
5660         if(this.selectOnFocus){
5661             this.on("focus", this.preFocus, this);
5662             
5663         }
5664         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5665             this.inputEl().on("keypress", this.filterKeys, this);
5666         }
5667        /* if(this.grow){
5668             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
5669             this.el.on("click", this.autoSize,  this);
5670         }
5671         */
5672         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5673             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5674         }
5675         
5676     },
5677     filterValidation : function(e){
5678         if(!e.isNavKeyPress()){
5679             this.validationTask.delay(this.validationDelay);
5680         }
5681     },
5682      /**
5683      * Validates the field value
5684      * @return {Boolean} True if the value is valid, else false
5685      */
5686     validate : function(){
5687         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5688         if(this.disabled || this.validateValue(this.getRawValue())){
5689             this.clearInvalid();
5690             return true;
5691         }
5692         return false;
5693     },
5694     
5695     
5696     /**
5697      * Validates a value according to the field's validation rules and marks the field as invalid
5698      * if the validation fails
5699      * @param {Mixed} value The value to validate
5700      * @return {Boolean} True if the value is valid, else false
5701      */
5702     validateValue : function(value){
5703         if(value.length < 1)  { // if it's blank
5704              if(this.allowBlank){
5705                 this.clearInvalid();
5706                 return true;
5707              }else{
5708                 this.markInvalid(this.blankText);
5709                 return false;
5710              }
5711         }
5712         if(value.length < this.minLength){
5713             this.markInvalid(String.format(this.minLengthText, this.minLength));
5714             return false;
5715         }
5716         if(value.length > this.maxLength){
5717             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5718             return false;
5719         }
5720         if(this.vtype){
5721             var vt = Roo.form.VTypes;
5722             if(!vt[this.vtype](value, this)){
5723                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5724                 return false;
5725             }
5726         }
5727         if(typeof this.validator == "function"){
5728             var msg = this.validator(value);
5729             if(msg !== true){
5730                 this.markInvalid(msg);
5731                 return false;
5732             }
5733         }
5734         if(this.regex && !this.regex.test(value)){
5735             this.markInvalid(this.regexText);
5736             return false;
5737         }
5738         return true;
5739     },
5740
5741     
5742     
5743      // private
5744     fireKey : function(e){
5745         //Roo.log('field ' + e.getKey());
5746         if(e.isNavKeyPress()){
5747             this.fireEvent("specialkey", this, e);
5748         }
5749     },
5750     focus : function (selectText){
5751         if(this.rendered){
5752             this.inputEl().focus();
5753             if(selectText === true){
5754                 this.inputEl().dom.select();
5755             }
5756         }
5757         return this;
5758     } ,
5759     
5760     onFocus : function(){
5761         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5762            // this.el.addClass(this.focusClass);
5763         }
5764         if(!this.hasFocus){
5765             this.hasFocus = true;
5766             this.startValue = this.getValue();
5767             this.fireEvent("focus", this);
5768         }
5769     },
5770     
5771     beforeBlur : Roo.emptyFn,
5772
5773     
5774     // private
5775     onBlur : function(){
5776         this.beforeBlur();
5777         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5778             //this.el.removeClass(this.focusClass);
5779         }
5780         this.hasFocus = false;
5781         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5782             this.validate();
5783         }
5784         var v = this.getValue();
5785         if(String(v) !== String(this.startValue)){
5786             this.fireEvent('change', this, v, this.startValue);
5787         }
5788         this.fireEvent("blur", this);
5789     },
5790     
5791     /**
5792      * Resets the current field value to the originally loaded value and clears any validation messages
5793      */
5794     reset : function(){
5795         this.setValue(this.originalValue);
5796         this.clearInvalid();
5797     },
5798      /**
5799      * Returns the name of the field
5800      * @return {Mixed} name The name field
5801      */
5802     getName: function(){
5803         return this.name;
5804     },
5805      /**
5806      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
5807      * @return {Mixed} value The field value
5808      */
5809     getValue : function(){
5810         return this.inputEl().getValue();
5811     },
5812     /**
5813      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
5814      * @return {Mixed} value The field value
5815      */
5816     getRawValue : function(){
5817         var v = this.inputEl().getValue();
5818         
5819         return v;
5820     },
5821     
5822     /**
5823      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
5824      * @param {Mixed} value The value to set
5825      */
5826     setRawValue : function(v){
5827         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5828     },
5829     
5830     selectText : function(start, end){
5831         var v = this.getRawValue();
5832         if(v.length > 0){
5833             start = start === undefined ? 0 : start;
5834             end = end === undefined ? v.length : end;
5835             var d = this.inputEl().dom;
5836             if(d.setSelectionRange){
5837                 d.setSelectionRange(start, end);
5838             }else if(d.createTextRange){
5839                 var range = d.createTextRange();
5840                 range.moveStart("character", start);
5841                 range.moveEnd("character", v.length-end);
5842                 range.select();
5843             }
5844         }
5845     },
5846     
5847     /**
5848      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
5849      * @param {Mixed} value The value to set
5850      */
5851     setValue : function(v){
5852         this.value = v;
5853         if(this.rendered){
5854             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5855             this.validate();
5856         }
5857     },
5858     
5859     /*
5860     processValue : function(value){
5861         if(this.stripCharsRe){
5862             var newValue = value.replace(this.stripCharsRe, '');
5863             if(newValue !== value){
5864                 this.setRawValue(newValue);
5865                 return newValue;
5866             }
5867         }
5868         return value;
5869     },
5870   */
5871     preFocus : function(){
5872         
5873         if(this.selectOnFocus){
5874             this.inputEl().dom.select();
5875         }
5876     },
5877     filterKeys : function(e){
5878         var k = e.getKey();
5879         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5880             return;
5881         }
5882         var c = e.getCharCode(), cc = String.fromCharCode(c);
5883         if(Roo.isIE && (e.isSpecialKey() || !cc)){
5884             return;
5885         }
5886         if(!this.maskRe.test(cc)){
5887             e.stopEvent();
5888         }
5889     },
5890      /**
5891      * Clear any invalid styles/messages for this field
5892      */
5893     clearInvalid : function(){
5894         
5895         if(!this.el || this.preventMark){ // not rendered
5896             return;
5897         }
5898         this.el.removeClass(this.invalidClass);
5899         /*
5900         switch(this.msgTarget){
5901             case 'qtip':
5902                 this.el.dom.qtip = '';
5903                 break;
5904             case 'title':
5905                 this.el.dom.title = '';
5906                 break;
5907             case 'under':
5908                 if(this.errorEl){
5909                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5910                 }
5911                 break;
5912             case 'side':
5913                 if(this.errorIcon){
5914                     this.errorIcon.dom.qtip = '';
5915                     this.errorIcon.hide();
5916                     this.un('resize', this.alignErrorIcon, this);
5917                 }
5918                 break;
5919             default:
5920                 var t = Roo.getDom(this.msgTarget);
5921                 t.innerHTML = '';
5922                 t.style.display = 'none';
5923                 break;
5924         }
5925         */
5926         this.fireEvent('valid', this);
5927     },
5928      /**
5929      * Mark this field as invalid
5930      * @param {String} msg The validation message
5931      */
5932     markInvalid : function(msg){
5933         if(!this.el  || this.preventMark){ // not rendered
5934             return;
5935         }
5936         this.el.addClass(this.invalidClass);
5937         /*
5938         msg = msg || this.invalidText;
5939         switch(this.msgTarget){
5940             case 'qtip':
5941                 this.el.dom.qtip = msg;
5942                 this.el.dom.qclass = 'x-form-invalid-tip';
5943                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5944                     Roo.QuickTips.enable();
5945                 }
5946                 break;
5947             case 'title':
5948                 this.el.dom.title = msg;
5949                 break;
5950             case 'under':
5951                 if(!this.errorEl){
5952                     var elp = this.el.findParent('.x-form-element', 5, true);
5953                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5954                     this.errorEl.setWidth(elp.getWidth(true)-20);
5955                 }
5956                 this.errorEl.update(msg);
5957                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5958                 break;
5959             case 'side':
5960                 if(!this.errorIcon){
5961                     var elp = this.el.findParent('.x-form-element', 5, true);
5962                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5963                 }
5964                 this.alignErrorIcon();
5965                 this.errorIcon.dom.qtip = msg;
5966                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5967                 this.errorIcon.show();
5968                 this.on('resize', this.alignErrorIcon, this);
5969                 break;
5970             default:
5971                 var t = Roo.getDom(this.msgTarget);
5972                 t.innerHTML = msg;
5973                 t.style.display = this.msgDisplay;
5974                 break;
5975         }
5976         */
5977         this.fireEvent('invalid', this, msg);
5978     },
5979     // private
5980     SafariOnKeyDown : function(event)
5981     {
5982         // this is a workaround for a password hang bug on chrome/ webkit.
5983         
5984         var isSelectAll = false;
5985         
5986         if(this.inputEl().dom.selectionEnd > 0){
5987             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5988         }
5989         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5990             event.preventDefault();
5991             this.setValue('');
5992             return;
5993         }
5994         
5995         if(isSelectAll){ // backspace and delete key
5996             
5997             event.preventDefault();
5998             // this is very hacky as keydown always get's upper case.
5999             //
6000             var cc = String.fromCharCode(event.getCharCode());
6001             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6002             
6003         }
6004     },
6005     adjustWidth : function(tag, w){
6006         tag = tag.toLowerCase();
6007         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6008             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6009                 if(tag == 'input'){
6010                     return w + 2;
6011                 }
6012                 if(tag == 'textarea'){
6013                     return w-2;
6014                 }
6015             }else if(Roo.isOpera){
6016                 if(tag == 'input'){
6017                     return w + 2;
6018                 }
6019                 if(tag == 'textarea'){
6020                     return w-2;
6021                 }
6022             }
6023         }
6024         return w;
6025     }
6026     
6027 });
6028
6029  
6030 /*
6031  * - LGPL
6032  *
6033  * Input
6034  * 
6035  */
6036
6037 /**
6038  * @class Roo.bootstrap.TextArea
6039  * @extends Roo.bootstrap.Input
6040  * Bootstrap TextArea class
6041  * @cfg {Number} cols Specifies the visible width of a text area
6042  * @cfg {Number} rows Specifies the visible number of lines in a text area
6043  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6044  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6045  * @cfg {string} html text
6046  * 
6047  * @constructor
6048  * Create a new TextArea
6049  * @param {Object} config The config object
6050  */
6051
6052 Roo.bootstrap.TextArea = function(config){
6053     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6054    
6055 };
6056
6057 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6058      
6059     cols : false,
6060     rows : 5,
6061     readOnly : false,
6062     warp : 'soft',
6063     resize : false,
6064     value: false,
6065     html: false,
6066     
6067     getAutoCreate : function(){
6068         
6069         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6070         
6071         var id = Roo.id();
6072         
6073         var cfg = {};
6074         
6075         var input =  {
6076             tag: 'textarea',
6077             id : id,
6078             warp : this.warp,
6079             rows : this.rows,
6080             value : this.value || '',
6081             html: this.html || '',
6082             cls : 'form-control',
6083             placeholder : this.placeholder || '' 
6084             
6085         };
6086         
6087         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6088             input.maxLength = this.maxLength;
6089         }
6090         
6091         if(this.resize){
6092             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6093         }
6094         
6095         if(this.cols){
6096             input.cols = this.cols;
6097         }
6098         
6099         if (this.readOnly) {
6100             input.readonly = true;
6101         }
6102         
6103         if (this.name) {
6104             input.name = this.name;
6105         }
6106         
6107         if (this.size) {
6108             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6109         }
6110         
6111         var settings=this;
6112         ['xs','sm','md','lg'].map(function(size){
6113             if (settings[size]) {
6114                 cfg.cls += ' col-' + size + '-' + settings[size];
6115             }
6116         });
6117         
6118         var inputblock = input;
6119         
6120         if (this.before || this.after) {
6121             
6122             inputblock = {
6123                 cls : 'input-group',
6124                 cn :  [] 
6125             };
6126             if (this.before) {
6127                 inputblock.cn.push({
6128                     tag :'span',
6129                     cls : 'input-group-addon',
6130                     html : this.before
6131                 });
6132             }
6133             inputblock.cn.push(input);
6134             if (this.after) {
6135                 inputblock.cn.push({
6136                     tag :'span',
6137                     cls : 'input-group-addon',
6138                     html : this.after
6139                 });
6140             }
6141             
6142         }
6143         
6144         if (align ==='left' && this.fieldLabel.length) {
6145                 Roo.log("left and has label");
6146                 cfg.cn = [
6147                     
6148                     {
6149                         tag: 'label',
6150                         'for' :  id,
6151                         cls : 'control-label col-sm-' + this.labelWidth,
6152                         html : this.fieldLabel
6153                         
6154                     },
6155                     {
6156                         cls : "col-sm-" + (12 - this.labelWidth), 
6157                         cn: [
6158                             inputblock
6159                         ]
6160                     }
6161                     
6162                 ];
6163         } else if ( this.fieldLabel.length) {
6164                 Roo.log(" label");
6165                  cfg.cn = [
6166                    
6167                     {
6168                         tag: 'label',
6169                         //cls : 'input-group-addon',
6170                         html : this.fieldLabel
6171                         
6172                     },
6173                     
6174                     inputblock
6175                     
6176                 ];
6177
6178         } else {
6179             
6180                    Roo.log(" no label && no align");
6181                 cfg.cn = [
6182                     
6183                         inputblock
6184                     
6185                 ];
6186                 
6187                 
6188         }
6189         
6190         if (this.disabled) {
6191             input.disabled=true;
6192         }
6193         
6194         return cfg;
6195         
6196     },
6197     /**
6198      * return the real textarea element.
6199      */
6200     inputEl: function ()
6201     {
6202         return this.el.select('textarea.form-control',true).first();
6203     }
6204 });
6205
6206  
6207 /*
6208  * - LGPL
6209  *
6210  * trigger field - base class for combo..
6211  * 
6212  */
6213  
6214 /**
6215  * @class Roo.bootstrap.TriggerField
6216  * @extends Roo.bootstrap.Input
6217  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6218  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6219  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6220  * for which you can provide a custom implementation.  For example:
6221  * <pre><code>
6222 var trigger = new Roo.bootstrap.TriggerField();
6223 trigger.onTriggerClick = myTriggerFn;
6224 trigger.applyTo('my-field');
6225 </code></pre>
6226  *
6227  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6228  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6229  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6230  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6231  * @constructor
6232  * Create a new TriggerField.
6233  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6234  * to the base TextField)
6235  */
6236 Roo.bootstrap.TriggerField = function(config){
6237     this.mimicing = false;
6238     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6239 };
6240
6241 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6242     /**
6243      * @cfg {String} triggerClass A CSS class to apply to the trigger
6244      */
6245      /**
6246      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6247      */
6248     hideTrigger:false,
6249
6250     /** @cfg {Boolean} grow @hide */
6251     /** @cfg {Number} growMin @hide */
6252     /** @cfg {Number} growMax @hide */
6253
6254     /**
6255      * @hide 
6256      * @method
6257      */
6258     autoSize: Roo.emptyFn,
6259     // private
6260     monitorTab : true,
6261     // private
6262     deferHeight : true,
6263
6264     
6265     actionMode : 'wrap',
6266     
6267     
6268     
6269     getAutoCreate : function(){
6270        
6271         var parent = this.parent();
6272         
6273         var align = this.parentLabelAlign();
6274         
6275         var id = Roo.id();
6276         
6277         var cfg = {
6278             cls: 'form-group' //input-group
6279         };
6280         
6281         
6282         var input =  {
6283             tag: 'input',
6284             id : id,
6285             type : this.inputType,
6286             cls : 'form-control',
6287             autocomplete: 'off',
6288             placeholder : this.placeholder || '' 
6289             
6290         };
6291         if (this.name) {
6292             input.name = this.name;
6293         }
6294         if (this.size) {
6295             input.cls += ' input-' + this.size;
6296         }
6297         
6298         if (this.disabled) {
6299             input.disabled=true;
6300         }
6301         
6302         var inputblock = input;
6303         
6304         if (this.before || this.after) {
6305             
6306             inputblock = {
6307                 cls : 'input-group',
6308                 cn :  [] 
6309             };
6310             if (this.before) {
6311                 inputblock.cn.push({
6312                     tag :'span',
6313                     cls : 'input-group-addon',
6314                     html : this.before
6315                 });
6316             }
6317             inputblock.cn.push(input);
6318             if (this.after) {
6319                 inputblock.cn.push({
6320                     tag :'span',
6321                     cls : 'input-group-addon',
6322                     html : this.after
6323                 });
6324             }
6325             
6326         };
6327         
6328         var box = {
6329             tag: 'div',
6330             cn: [
6331                 {
6332                     tag: 'input',
6333                     type : 'hidden',
6334                     cls: 'form-hidden-field'
6335                 },
6336                 inputblock
6337             ]
6338             
6339         };
6340         
6341         if(this.multiple){
6342             Roo.log('multiple');
6343             
6344             box = {
6345                 tag: 'div',
6346                 cn: [
6347                     {
6348                         tag: 'input',
6349                         type : 'hidden',
6350                         cls: 'form-hidden-field'
6351                     },
6352                     {
6353                         tag: 'ul',
6354                         cls: 'select2-choices',
6355                         cn:[
6356                             {
6357                                 tag: 'li',
6358                                 cls: 'select2-search-field',
6359                                 cn: [
6360
6361                                     inputblock
6362                                 ]
6363                             }
6364                         ]
6365                     }
6366                 ]
6367             }
6368         };
6369         
6370         var combobox = {
6371             cls: 'select2-container input-group',
6372             cn: [
6373                 box,
6374                 {
6375                     tag: 'ul',
6376                     cls: 'typeahead typeahead-long dropdown-menu',
6377                     style: 'display:none'
6378                 }
6379             ]
6380         };
6381         
6382         if(!this.multiple){
6383             combobox.cn.push({
6384                 tag :'span',
6385                 cls : 'input-group-addon btn dropdown-toggle',
6386                 cn : [
6387                     {
6388                         tag: 'span',
6389                         cls: 'caret'
6390                     },
6391                     {
6392                         tag: 'span',
6393                         cls: 'combobox-clear',
6394                         cn  : [
6395                             {
6396                                 tag : 'i',
6397                                 cls: 'icon-remove'
6398                             }
6399                         ]
6400                     }
6401                 ]
6402
6403             })
6404         }
6405         
6406         if(this.multiple){
6407             combobox.cls += ' select2-container-multi';
6408         }
6409         
6410         if (align ==='left' && this.fieldLabel.length) {
6411             
6412                 Roo.log("left and has label");
6413                 cfg.cn = [
6414                     
6415                     {
6416                         tag: 'label',
6417                         'for' :  id,
6418                         cls : 'control-label col-sm-' + this.labelWidth,
6419                         html : this.fieldLabel
6420                         
6421                     },
6422                     {
6423                         cls : "col-sm-" + (12 - this.labelWidth), 
6424                         cn: [
6425                             combobox
6426                         ]
6427                     }
6428                     
6429                 ];
6430         } else if ( this.fieldLabel.length) {
6431                 Roo.log(" label");
6432                  cfg.cn = [
6433                    
6434                     {
6435                         tag: 'label',
6436                         //cls : 'input-group-addon',
6437                         html : this.fieldLabel
6438                         
6439                     },
6440                     
6441                     combobox
6442                     
6443                 ];
6444
6445         } else {
6446             
6447                 Roo.log(" no label && no align");
6448                 cfg = combobox
6449                      
6450                 
6451         }
6452          
6453         var settings=this;
6454         ['xs','sm','md','lg'].map(function(size){
6455             if (settings[size]) {
6456                 cfg.cls += ' col-' + size + '-' + settings[size];
6457             }
6458         });
6459         
6460         return cfg;
6461         
6462     },
6463     
6464     
6465     
6466     // private
6467     onResize : function(w, h){
6468 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6469 //        if(typeof w == 'number'){
6470 //            var x = w - this.trigger.getWidth();
6471 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6472 //            this.trigger.setStyle('left', x+'px');
6473 //        }
6474     },
6475
6476     // private
6477     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6478
6479     // private
6480     getResizeEl : function(){
6481         return this.inputEl();
6482     },
6483
6484     // private
6485     getPositionEl : function(){
6486         return this.inputEl();
6487     },
6488
6489     // private
6490     alignErrorIcon : function(){
6491         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6492     },
6493
6494     // private
6495     initEvents : function(){
6496         
6497         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6498         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6499         if(!this.multiple){
6500             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6501             if(this.hideTrigger){
6502                 this.trigger.setDisplayed(false);
6503             }
6504             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6505         }
6506         
6507         if(this.multiple){
6508             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6509         }
6510         
6511         //this.trigger.addClassOnOver('x-form-trigger-over');
6512         //this.trigger.addClassOnClick('x-form-trigger-click');
6513         
6514         //if(!this.width){
6515         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6516         //}
6517     },
6518
6519     // private
6520     initTrigger : function(){
6521        
6522     },
6523
6524     // private
6525     onDestroy : function(){
6526         if(this.trigger){
6527             this.trigger.removeAllListeners();
6528           //  this.trigger.remove();
6529         }
6530         //if(this.wrap){
6531         //    this.wrap.remove();
6532         //}
6533         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6534     },
6535
6536     // private
6537     onFocus : function(){
6538         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6539         /*
6540         if(!this.mimicing){
6541             this.wrap.addClass('x-trigger-wrap-focus');
6542             this.mimicing = true;
6543             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6544             if(this.monitorTab){
6545                 this.el.on("keydown", this.checkTab, this);
6546             }
6547         }
6548         */
6549     },
6550
6551     // private
6552     checkTab : function(e){
6553         if(e.getKey() == e.TAB){
6554             this.triggerBlur();
6555         }
6556     },
6557
6558     // private
6559     onBlur : function(){
6560         // do nothing
6561     },
6562
6563     // private
6564     mimicBlur : function(e, t){
6565         /*
6566         if(!this.wrap.contains(t) && this.validateBlur()){
6567             this.triggerBlur();
6568         }
6569         */
6570     },
6571
6572     // private
6573     triggerBlur : function(){
6574         this.mimicing = false;
6575         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6576         if(this.monitorTab){
6577             this.el.un("keydown", this.checkTab, this);
6578         }
6579         //this.wrap.removeClass('x-trigger-wrap-focus');
6580         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6581     },
6582
6583     // private
6584     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6585     validateBlur : function(e, t){
6586         return true;
6587     },
6588
6589     // private
6590     onDisable : function(){
6591         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6592         //if(this.wrap){
6593         //    this.wrap.addClass('x-item-disabled');
6594         //}
6595     },
6596
6597     // private
6598     onEnable : function(){
6599         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6600         //if(this.wrap){
6601         //    this.el.removeClass('x-item-disabled');
6602         //}
6603     },
6604
6605     // private
6606     onShow : function(){
6607         var ae = this.getActionEl();
6608         
6609         if(ae){
6610             ae.dom.style.display = '';
6611             ae.dom.style.visibility = 'visible';
6612         }
6613     },
6614
6615     // private
6616     
6617     onHide : function(){
6618         var ae = this.getActionEl();
6619         ae.dom.style.display = 'none';
6620     },
6621
6622     /**
6623      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6624      * by an implementing function.
6625      * @method
6626      * @param {EventObject} e
6627      */
6628     onTriggerClick : Roo.emptyFn
6629 });
6630  /*
6631  * Based on:
6632  * Ext JS Library 1.1.1
6633  * Copyright(c) 2006-2007, Ext JS, LLC.
6634  *
6635  * Originally Released Under LGPL - original licence link has changed is not relivant.
6636  *
6637  * Fork - LGPL
6638  * <script type="text/javascript">
6639  */
6640
6641
6642 /**
6643  * @class Roo.data.SortTypes
6644  * @singleton
6645  * Defines the default sorting (casting?) comparison functions used when sorting data.
6646  */
6647 Roo.data.SortTypes = {
6648     /**
6649      * Default sort that does nothing
6650      * @param {Mixed} s The value being converted
6651      * @return {Mixed} The comparison value
6652      */
6653     none : function(s){
6654         return s;
6655     },
6656     
6657     /**
6658      * The regular expression used to strip tags
6659      * @type {RegExp}
6660      * @property
6661      */
6662     stripTagsRE : /<\/?[^>]+>/gi,
6663     
6664     /**
6665      * Strips all HTML tags to sort on text only
6666      * @param {Mixed} s The value being converted
6667      * @return {String} The comparison value
6668      */
6669     asText : function(s){
6670         return String(s).replace(this.stripTagsRE, "");
6671     },
6672     
6673     /**
6674      * Strips all HTML tags to sort on text only - Case insensitive
6675      * @param {Mixed} s The value being converted
6676      * @return {String} The comparison value
6677      */
6678     asUCText : function(s){
6679         return String(s).toUpperCase().replace(this.stripTagsRE, "");
6680     },
6681     
6682     /**
6683      * Case insensitive string
6684      * @param {Mixed} s The value being converted
6685      * @return {String} The comparison value
6686      */
6687     asUCString : function(s) {
6688         return String(s).toUpperCase();
6689     },
6690     
6691     /**
6692      * Date sorting
6693      * @param {Mixed} s The value being converted
6694      * @return {Number} The comparison value
6695      */
6696     asDate : function(s) {
6697         if(!s){
6698             return 0;
6699         }
6700         if(s instanceof Date){
6701             return s.getTime();
6702         }
6703         return Date.parse(String(s));
6704     },
6705     
6706     /**
6707      * Float sorting
6708      * @param {Mixed} s The value being converted
6709      * @return {Float} The comparison value
6710      */
6711     asFloat : function(s) {
6712         var val = parseFloat(String(s).replace(/,/g, ""));
6713         if(isNaN(val)) val = 0;
6714         return val;
6715     },
6716     
6717     /**
6718      * Integer sorting
6719      * @param {Mixed} s The value being converted
6720      * @return {Number} The comparison value
6721      */
6722     asInt : function(s) {
6723         var val = parseInt(String(s).replace(/,/g, ""));
6724         if(isNaN(val)) val = 0;
6725         return val;
6726     }
6727 };/*
6728  * Based on:
6729  * Ext JS Library 1.1.1
6730  * Copyright(c) 2006-2007, Ext JS, LLC.
6731  *
6732  * Originally Released Under LGPL - original licence link has changed is not relivant.
6733  *
6734  * Fork - LGPL
6735  * <script type="text/javascript">
6736  */
6737
6738 /**
6739 * @class Roo.data.Record
6740  * Instances of this class encapsulate both record <em>definition</em> information, and record
6741  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
6742  * to access Records cached in an {@link Roo.data.Store} object.<br>
6743  * <p>
6744  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
6745  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
6746  * objects.<br>
6747  * <p>
6748  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
6749  * @constructor
6750  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
6751  * {@link #create}. The parameters are the same.
6752  * @param {Array} data An associative Array of data values keyed by the field name.
6753  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
6754  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
6755  * not specified an integer id is generated.
6756  */
6757 Roo.data.Record = function(data, id){
6758     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
6759     this.data = data;
6760 };
6761
6762 /**
6763  * Generate a constructor for a specific record layout.
6764  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
6765  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
6766  * Each field definition object may contain the following properties: <ul>
6767  * <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,
6768  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
6769  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
6770  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
6771  * is being used, then this is a string containing the javascript expression to reference the data relative to 
6772  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
6773  * to the data item relative to the record element. If the mapping expression is the same as the field name,
6774  * this may be omitted.</p></li>
6775  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
6776  * <ul><li>auto (Default, implies no conversion)</li>
6777  * <li>string</li>
6778  * <li>int</li>
6779  * <li>float</li>
6780  * <li>boolean</li>
6781  * <li>date</li></ul></p></li>
6782  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6783  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6784  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6785  * by the Reader into an object that will be stored in the Record. It is passed the
6786  * following parameters:<ul>
6787  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6788  * </ul></p></li>
6789  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6790  * </ul>
6791  * <br>usage:<br><pre><code>
6792 var TopicRecord = Roo.data.Record.create(
6793     {name: 'title', mapping: 'topic_title'},
6794     {name: 'author', mapping: 'username'},
6795     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6796     {name: 'lastPost', mapping: 'post_time', type: 'date'},
6797     {name: 'lastPoster', mapping: 'user2'},
6798     {name: 'excerpt', mapping: 'post_text'}
6799 );
6800
6801 var myNewRecord = new TopicRecord({
6802     title: 'Do my job please',
6803     author: 'noobie',
6804     totalPosts: 1,
6805     lastPost: new Date(),
6806     lastPoster: 'Animal',
6807     excerpt: 'No way dude!'
6808 });
6809 myStore.add(myNewRecord);
6810 </code></pre>
6811  * @method create
6812  * @static
6813  */
6814 Roo.data.Record.create = function(o){
6815     var f = function(){
6816         f.superclass.constructor.apply(this, arguments);
6817     };
6818     Roo.extend(f, Roo.data.Record);
6819     var p = f.prototype;
6820     p.fields = new Roo.util.MixedCollection(false, function(field){
6821         return field.name;
6822     });
6823     for(var i = 0, len = o.length; i < len; i++){
6824         p.fields.add(new Roo.data.Field(o[i]));
6825     }
6826     f.getField = function(name){
6827         return p.fields.get(name);  
6828     };
6829     return f;
6830 };
6831
6832 Roo.data.Record.AUTO_ID = 1000;
6833 Roo.data.Record.EDIT = 'edit';
6834 Roo.data.Record.REJECT = 'reject';
6835 Roo.data.Record.COMMIT = 'commit';
6836
6837 Roo.data.Record.prototype = {
6838     /**
6839      * Readonly flag - true if this record has been modified.
6840      * @type Boolean
6841      */
6842     dirty : false,
6843     editing : false,
6844     error: null,
6845     modified: null,
6846
6847     // private
6848     join : function(store){
6849         this.store = store;
6850     },
6851
6852     /**
6853      * Set the named field to the specified value.
6854      * @param {String} name The name of the field to set.
6855      * @param {Object} value The value to set the field to.
6856      */
6857     set : function(name, value){
6858         if(this.data[name] == value){
6859             return;
6860         }
6861         this.dirty = true;
6862         if(!this.modified){
6863             this.modified = {};
6864         }
6865         if(typeof this.modified[name] == 'undefined'){
6866             this.modified[name] = this.data[name];
6867         }
6868         this.data[name] = value;
6869         if(!this.editing && this.store){
6870             this.store.afterEdit(this);
6871         }       
6872     },
6873
6874     /**
6875      * Get the value of the named field.
6876      * @param {String} name The name of the field to get the value of.
6877      * @return {Object} The value of the field.
6878      */
6879     get : function(name){
6880         return this.data[name]; 
6881     },
6882
6883     // private
6884     beginEdit : function(){
6885         this.editing = true;
6886         this.modified = {}; 
6887     },
6888
6889     // private
6890     cancelEdit : function(){
6891         this.editing = false;
6892         delete this.modified;
6893     },
6894
6895     // private
6896     endEdit : function(){
6897         this.editing = false;
6898         if(this.dirty && this.store){
6899             this.store.afterEdit(this);
6900         }
6901     },
6902
6903     /**
6904      * Usually called by the {@link Roo.data.Store} which owns the Record.
6905      * Rejects all changes made to the Record since either creation, or the last commit operation.
6906      * Modified fields are reverted to their original values.
6907      * <p>
6908      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6909      * of reject operations.
6910      */
6911     reject : function(){
6912         var m = this.modified;
6913         for(var n in m){
6914             if(typeof m[n] != "function"){
6915                 this.data[n] = m[n];
6916             }
6917         }
6918         this.dirty = false;
6919         delete this.modified;
6920         this.editing = false;
6921         if(this.store){
6922             this.store.afterReject(this);
6923         }
6924     },
6925
6926     /**
6927      * Usually called by the {@link Roo.data.Store} which owns the Record.
6928      * Commits all changes made to the Record since either creation, or the last commit operation.
6929      * <p>
6930      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6931      * of commit operations.
6932      */
6933     commit : function(){
6934         this.dirty = false;
6935         delete this.modified;
6936         this.editing = false;
6937         if(this.store){
6938             this.store.afterCommit(this);
6939         }
6940     },
6941
6942     // private
6943     hasError : function(){
6944         return this.error != null;
6945     },
6946
6947     // private
6948     clearError : function(){
6949         this.error = null;
6950     },
6951
6952     /**
6953      * Creates a copy of this record.
6954      * @param {String} id (optional) A new record id if you don't want to use this record's id
6955      * @return {Record}
6956      */
6957     copy : function(newId) {
6958         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6959     }
6960 };/*
6961  * Based on:
6962  * Ext JS Library 1.1.1
6963  * Copyright(c) 2006-2007, Ext JS, LLC.
6964  *
6965  * Originally Released Under LGPL - original licence link has changed is not relivant.
6966  *
6967  * Fork - LGPL
6968  * <script type="text/javascript">
6969  */
6970
6971
6972
6973 /**
6974  * @class Roo.data.Store
6975  * @extends Roo.util.Observable
6976  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6977  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6978  * <p>
6979  * 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
6980  * has no knowledge of the format of the data returned by the Proxy.<br>
6981  * <p>
6982  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6983  * instances from the data object. These records are cached and made available through accessor functions.
6984  * @constructor
6985  * Creates a new Store.
6986  * @param {Object} config A config object containing the objects needed for the Store to access data,
6987  * and read the data into Records.
6988  */
6989 Roo.data.Store = function(config){
6990     this.data = new Roo.util.MixedCollection(false);
6991     this.data.getKey = function(o){
6992         return o.id;
6993     };
6994     this.baseParams = {};
6995     // private
6996     this.paramNames = {
6997         "start" : "start",
6998         "limit" : "limit",
6999         "sort" : "sort",
7000         "dir" : "dir",
7001         "multisort" : "_multisort"
7002     };
7003
7004     if(config && config.data){
7005         this.inlineData = config.data;
7006         delete config.data;
7007     }
7008
7009     Roo.apply(this, config);
7010     
7011     if(this.reader){ // reader passed
7012         this.reader = Roo.factory(this.reader, Roo.data);
7013         this.reader.xmodule = this.xmodule || false;
7014         if(!this.recordType){
7015             this.recordType = this.reader.recordType;
7016         }
7017         if(this.reader.onMetaChange){
7018             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7019         }
7020     }
7021
7022     if(this.recordType){
7023         this.fields = this.recordType.prototype.fields;
7024     }
7025     this.modified = [];
7026
7027     this.addEvents({
7028         /**
7029          * @event datachanged
7030          * Fires when the data cache has changed, and a widget which is using this Store
7031          * as a Record cache should refresh its view.
7032          * @param {Store} this
7033          */
7034         datachanged : true,
7035         /**
7036          * @event metachange
7037          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7038          * @param {Store} this
7039          * @param {Object} meta The JSON metadata
7040          */
7041         metachange : true,
7042         /**
7043          * @event add
7044          * Fires when Records have been added to the Store
7045          * @param {Store} this
7046          * @param {Roo.data.Record[]} records The array of Records added
7047          * @param {Number} index The index at which the record(s) were added
7048          */
7049         add : true,
7050         /**
7051          * @event remove
7052          * Fires when a Record has been removed from the Store
7053          * @param {Store} this
7054          * @param {Roo.data.Record} record The Record that was removed
7055          * @param {Number} index The index at which the record was removed
7056          */
7057         remove : true,
7058         /**
7059          * @event update
7060          * Fires when a Record has been updated
7061          * @param {Store} this
7062          * @param {Roo.data.Record} record The Record that was updated
7063          * @param {String} operation The update operation being performed.  Value may be one of:
7064          * <pre><code>
7065  Roo.data.Record.EDIT
7066  Roo.data.Record.REJECT
7067  Roo.data.Record.COMMIT
7068          * </code></pre>
7069          */
7070         update : true,
7071         /**
7072          * @event clear
7073          * Fires when the data cache has been cleared.
7074          * @param {Store} this
7075          */
7076         clear : true,
7077         /**
7078          * @event beforeload
7079          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7080          * the load action will be canceled.
7081          * @param {Store} this
7082          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7083          */
7084         beforeload : true,
7085         /**
7086          * @event beforeloadadd
7087          * Fires after a new set of Records has been loaded.
7088          * @param {Store} this
7089          * @param {Roo.data.Record[]} records The Records that were loaded
7090          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7091          */
7092         beforeloadadd : true,
7093         /**
7094          * @event load
7095          * Fires after a new set of Records has been loaded, before they are added to the store.
7096          * @param {Store} this
7097          * @param {Roo.data.Record[]} records The Records that were loaded
7098          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7099          * @params {Object} return from reader
7100          */
7101         load : true,
7102         /**
7103          * @event loadexception
7104          * Fires if an exception occurs in the Proxy during loading.
7105          * Called with the signature of the Proxy's "loadexception" event.
7106          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7107          * 
7108          * @param {Proxy} 
7109          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7110          * @param {Object} load options 
7111          * @param {Object} jsonData from your request (normally this contains the Exception)
7112          */
7113         loadexception : true
7114     });
7115     
7116     if(this.proxy){
7117         this.proxy = Roo.factory(this.proxy, Roo.data);
7118         this.proxy.xmodule = this.xmodule || false;
7119         this.relayEvents(this.proxy,  ["loadexception"]);
7120     }
7121     this.sortToggle = {};
7122     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7123
7124     Roo.data.Store.superclass.constructor.call(this);
7125
7126     if(this.inlineData){
7127         this.loadData(this.inlineData);
7128         delete this.inlineData;
7129     }
7130 };
7131
7132 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7133      /**
7134     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7135     * without a remote query - used by combo/forms at present.
7136     */
7137     
7138     /**
7139     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7140     */
7141     /**
7142     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7143     */
7144     /**
7145     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7146     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7147     */
7148     /**
7149     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7150     * on any HTTP request
7151     */
7152     /**
7153     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7154     */
7155     /**
7156     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7157     */
7158     multiSort: false,
7159     /**
7160     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7161     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7162     */
7163     remoteSort : false,
7164
7165     /**
7166     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7167      * loaded or when a record is removed. (defaults to false).
7168     */
7169     pruneModifiedRecords : false,
7170
7171     // private
7172     lastOptions : null,
7173
7174     /**
7175      * Add Records to the Store and fires the add event.
7176      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7177      */
7178     add : function(records){
7179         records = [].concat(records);
7180         for(var i = 0, len = records.length; i < len; i++){
7181             records[i].join(this);
7182         }
7183         var index = this.data.length;
7184         this.data.addAll(records);
7185         this.fireEvent("add", this, records, index);
7186     },
7187
7188     /**
7189      * Remove a Record from the Store and fires the remove event.
7190      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7191      */
7192     remove : function(record){
7193         var index = this.data.indexOf(record);
7194         this.data.removeAt(index);
7195         if(this.pruneModifiedRecords){
7196             this.modified.remove(record);
7197         }
7198         this.fireEvent("remove", this, record, index);
7199     },
7200
7201     /**
7202      * Remove all Records from the Store and fires the clear event.
7203      */
7204     removeAll : function(){
7205         this.data.clear();
7206         if(this.pruneModifiedRecords){
7207             this.modified = [];
7208         }
7209         this.fireEvent("clear", this);
7210     },
7211
7212     /**
7213      * Inserts Records to the Store at the given index and fires the add event.
7214      * @param {Number} index The start index at which to insert the passed Records.
7215      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7216      */
7217     insert : function(index, records){
7218         records = [].concat(records);
7219         for(var i = 0, len = records.length; i < len; i++){
7220             this.data.insert(index, records[i]);
7221             records[i].join(this);
7222         }
7223         this.fireEvent("add", this, records, index);
7224     },
7225
7226     /**
7227      * Get the index within the cache of the passed Record.
7228      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7229      * @return {Number} The index of the passed Record. Returns -1 if not found.
7230      */
7231     indexOf : function(record){
7232         return this.data.indexOf(record);
7233     },
7234
7235     /**
7236      * Get the index within the cache of the Record with the passed id.
7237      * @param {String} id The id of the Record to find.
7238      * @return {Number} The index of the Record. Returns -1 if not found.
7239      */
7240     indexOfId : function(id){
7241         return this.data.indexOfKey(id);
7242     },
7243
7244     /**
7245      * Get the Record with the specified id.
7246      * @param {String} id The id of the Record to find.
7247      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7248      */
7249     getById : function(id){
7250         return this.data.key(id);
7251     },
7252
7253     /**
7254      * Get the Record at the specified index.
7255      * @param {Number} index The index of the Record to find.
7256      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7257      */
7258     getAt : function(index){
7259         return this.data.itemAt(index);
7260     },
7261
7262     /**
7263      * Returns a range of Records between specified indices.
7264      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7265      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7266      * @return {Roo.data.Record[]} An array of Records
7267      */
7268     getRange : function(start, end){
7269         return this.data.getRange(start, end);
7270     },
7271
7272     // private
7273     storeOptions : function(o){
7274         o = Roo.apply({}, o);
7275         delete o.callback;
7276         delete o.scope;
7277         this.lastOptions = o;
7278     },
7279
7280     /**
7281      * Loads the Record cache from the configured Proxy using the configured Reader.
7282      * <p>
7283      * If using remote paging, then the first load call must specify the <em>start</em>
7284      * and <em>limit</em> properties in the options.params property to establish the initial
7285      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7286      * <p>
7287      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7288      * and this call will return before the new data has been loaded. Perform any post-processing
7289      * in a callback function, or in a "load" event handler.</strong>
7290      * <p>
7291      * @param {Object} options An object containing properties which control loading options:<ul>
7292      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7293      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7294      * passed the following arguments:<ul>
7295      * <li>r : Roo.data.Record[]</li>
7296      * <li>options: Options object from the load call</li>
7297      * <li>success: Boolean success indicator</li></ul></li>
7298      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7299      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7300      * </ul>
7301      */
7302     load : function(options){
7303         options = options || {};
7304         if(this.fireEvent("beforeload", this, options) !== false){
7305             this.storeOptions(options);
7306             var p = Roo.apply(options.params || {}, this.baseParams);
7307             // if meta was not loaded from remote source.. try requesting it.
7308             if (!this.reader.metaFromRemote) {
7309                 p._requestMeta = 1;
7310             }
7311             if(this.sortInfo && this.remoteSort){
7312                 var pn = this.paramNames;
7313                 p[pn["sort"]] = this.sortInfo.field;
7314                 p[pn["dir"]] = this.sortInfo.direction;
7315             }
7316             if (this.multiSort) {
7317                 var pn = this.paramNames;
7318                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7319             }
7320             
7321             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7322         }
7323     },
7324
7325     /**
7326      * Reloads the Record cache from the configured Proxy using the configured Reader and
7327      * the options from the last load operation performed.
7328      * @param {Object} options (optional) An object containing properties which may override the options
7329      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7330      * the most recently used options are reused).
7331      */
7332     reload : function(options){
7333         this.load(Roo.applyIf(options||{}, this.lastOptions));
7334     },
7335
7336     // private
7337     // Called as a callback by the Reader during a load operation.
7338     loadRecords : function(o, options, success){
7339         if(!o || success === false){
7340             if(success !== false){
7341                 this.fireEvent("load", this, [], options, o);
7342             }
7343             if(options.callback){
7344                 options.callback.call(options.scope || this, [], options, false);
7345             }
7346             return;
7347         }
7348         // if data returned failure - throw an exception.
7349         if (o.success === false) {
7350             // show a message if no listener is registered.
7351             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7352                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7353             }
7354             // loadmask wil be hooked into this..
7355             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7356             return;
7357         }
7358         var r = o.records, t = o.totalRecords || r.length;
7359         
7360         this.fireEvent("beforeloadadd", this, r, options, o);
7361         
7362         if(!options || options.add !== true){
7363             if(this.pruneModifiedRecords){
7364                 this.modified = [];
7365             }
7366             for(var i = 0, len = r.length; i < len; i++){
7367                 r[i].join(this);
7368             }
7369             if(this.snapshot){
7370                 this.data = this.snapshot;
7371                 delete this.snapshot;
7372             }
7373             this.data.clear();
7374             this.data.addAll(r);
7375             this.totalLength = t;
7376             this.applySort();
7377             this.fireEvent("datachanged", this);
7378         }else{
7379             this.totalLength = Math.max(t, this.data.length+r.length);
7380             this.add(r);
7381         }
7382         this.fireEvent("load", this, r, options, o);
7383         if(options.callback){
7384             options.callback.call(options.scope || this, r, options, true);
7385         }
7386     },
7387
7388
7389     /**
7390      * Loads data from a passed data block. A Reader which understands the format of the data
7391      * must have been configured in the constructor.
7392      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7393      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7394      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7395      */
7396     loadData : function(o, append){
7397         var r = this.reader.readRecords(o);
7398         this.loadRecords(r, {add: append}, true);
7399     },
7400
7401     /**
7402      * Gets the number of cached records.
7403      * <p>
7404      * <em>If using paging, this may not be the total size of the dataset. If the data object
7405      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7406      * the data set size</em>
7407      */
7408     getCount : function(){
7409         return this.data.length || 0;
7410     },
7411
7412     /**
7413      * Gets the total number of records in the dataset as returned by the server.
7414      * <p>
7415      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7416      * the dataset size</em>
7417      */
7418     getTotalCount : function(){
7419         return this.totalLength || 0;
7420     },
7421
7422     /**
7423      * Returns the sort state of the Store as an object with two properties:
7424      * <pre><code>
7425  field {String} The name of the field by which the Records are sorted
7426  direction {String} The sort order, "ASC" or "DESC"
7427      * </code></pre>
7428      */
7429     getSortState : function(){
7430         return this.sortInfo;
7431     },
7432
7433     // private
7434     applySort : function(){
7435         if(this.sortInfo && !this.remoteSort){
7436             var s = this.sortInfo, f = s.field;
7437             var st = this.fields.get(f).sortType;
7438             var fn = function(r1, r2){
7439                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7440                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7441             };
7442             this.data.sort(s.direction, fn);
7443             if(this.snapshot && this.snapshot != this.data){
7444                 this.snapshot.sort(s.direction, fn);
7445             }
7446         }
7447     },
7448
7449     /**
7450      * Sets the default sort column and order to be used by the next load operation.
7451      * @param {String} fieldName The name of the field to sort by.
7452      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7453      */
7454     setDefaultSort : function(field, dir){
7455         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7456     },
7457
7458     /**
7459      * Sort the Records.
7460      * If remote sorting is used, the sort is performed on the server, and the cache is
7461      * reloaded. If local sorting is used, the cache is sorted internally.
7462      * @param {String} fieldName The name of the field to sort by.
7463      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7464      */
7465     sort : function(fieldName, dir){
7466         var f = this.fields.get(fieldName);
7467         if(!dir){
7468             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7469             
7470             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7471                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7472             }else{
7473                 dir = f.sortDir;
7474             }
7475         }
7476         this.sortToggle[f.name] = dir;
7477         this.sortInfo = {field: f.name, direction: dir};
7478         if(!this.remoteSort){
7479             this.applySort();
7480             this.fireEvent("datachanged", this);
7481         }else{
7482             this.load(this.lastOptions);
7483         }
7484     },
7485
7486     /**
7487      * Calls the specified function for each of the Records in the cache.
7488      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7489      * Returning <em>false</em> aborts and exits the iteration.
7490      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7491      */
7492     each : function(fn, scope){
7493         this.data.each(fn, scope);
7494     },
7495
7496     /**
7497      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7498      * (e.g., during paging).
7499      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7500      */
7501     getModifiedRecords : function(){
7502         return this.modified;
7503     },
7504
7505     // private
7506     createFilterFn : function(property, value, anyMatch){
7507         if(!value.exec){ // not a regex
7508             value = String(value);
7509             if(value.length == 0){
7510                 return false;
7511             }
7512             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7513         }
7514         return function(r){
7515             return value.test(r.data[property]);
7516         };
7517     },
7518
7519     /**
7520      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7521      * @param {String} property A field on your records
7522      * @param {Number} start The record index to start at (defaults to 0)
7523      * @param {Number} end The last record index to include (defaults to length - 1)
7524      * @return {Number} The sum
7525      */
7526     sum : function(property, start, end){
7527         var rs = this.data.items, v = 0;
7528         start = start || 0;
7529         end = (end || end === 0) ? end : rs.length-1;
7530
7531         for(var i = start; i <= end; i++){
7532             v += (rs[i].data[property] || 0);
7533         }
7534         return v;
7535     },
7536
7537     /**
7538      * Filter the records by a specified property.
7539      * @param {String} field A field on your records
7540      * @param {String/RegExp} value Either a string that the field
7541      * should start with or a RegExp to test against the field
7542      * @param {Boolean} anyMatch True to match any part not just the beginning
7543      */
7544     filter : function(property, value, anyMatch){
7545         var fn = this.createFilterFn(property, value, anyMatch);
7546         return fn ? this.filterBy(fn) : this.clearFilter();
7547     },
7548
7549     /**
7550      * Filter by a function. The specified function will be called with each
7551      * record in this data source. If the function returns true the record is included,
7552      * otherwise it is filtered.
7553      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7554      * @param {Object} scope (optional) The scope of the function (defaults to this)
7555      */
7556     filterBy : function(fn, scope){
7557         this.snapshot = this.snapshot || this.data;
7558         this.data = this.queryBy(fn, scope||this);
7559         this.fireEvent("datachanged", this);
7560     },
7561
7562     /**
7563      * Query the records by a specified property.
7564      * @param {String} field A field on your records
7565      * @param {String/RegExp} value Either a string that the field
7566      * should start with or a RegExp to test against the field
7567      * @param {Boolean} anyMatch True to match any part not just the beginning
7568      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7569      */
7570     query : function(property, value, anyMatch){
7571         var fn = this.createFilterFn(property, value, anyMatch);
7572         return fn ? this.queryBy(fn) : this.data.clone();
7573     },
7574
7575     /**
7576      * Query by a function. The specified function will be called with each
7577      * record in this data source. If the function returns true the record is included
7578      * in the results.
7579      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7580      * @param {Object} scope (optional) The scope of the function (defaults to this)
7581       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7582      **/
7583     queryBy : function(fn, scope){
7584         var data = this.snapshot || this.data;
7585         return data.filterBy(fn, scope||this);
7586     },
7587
7588     /**
7589      * Collects unique values for a particular dataIndex from this store.
7590      * @param {String} dataIndex The property to collect
7591      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7592      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7593      * @return {Array} An array of the unique values
7594      **/
7595     collect : function(dataIndex, allowNull, bypassFilter){
7596         var d = (bypassFilter === true && this.snapshot) ?
7597                 this.snapshot.items : this.data.items;
7598         var v, sv, r = [], l = {};
7599         for(var i = 0, len = d.length; i < len; i++){
7600             v = d[i].data[dataIndex];
7601             sv = String(v);
7602             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7603                 l[sv] = true;
7604                 r[r.length] = v;
7605             }
7606         }
7607         return r;
7608     },
7609
7610     /**
7611      * Revert to a view of the Record cache with no filtering applied.
7612      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7613      */
7614     clearFilter : function(suppressEvent){
7615         if(this.snapshot && this.snapshot != this.data){
7616             this.data = this.snapshot;
7617             delete this.snapshot;
7618             if(suppressEvent !== true){
7619                 this.fireEvent("datachanged", this);
7620             }
7621         }
7622     },
7623
7624     // private
7625     afterEdit : function(record){
7626         if(this.modified.indexOf(record) == -1){
7627             this.modified.push(record);
7628         }
7629         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7630     },
7631     
7632     // private
7633     afterReject : function(record){
7634         this.modified.remove(record);
7635         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7636     },
7637
7638     // private
7639     afterCommit : function(record){
7640         this.modified.remove(record);
7641         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7642     },
7643
7644     /**
7645      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7646      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7647      */
7648     commitChanges : function(){
7649         var m = this.modified.slice(0);
7650         this.modified = [];
7651         for(var i = 0, len = m.length; i < len; i++){
7652             m[i].commit();
7653         }
7654     },
7655
7656     /**
7657      * Cancel outstanding changes on all changed records.
7658      */
7659     rejectChanges : function(){
7660         var m = this.modified.slice(0);
7661         this.modified = [];
7662         for(var i = 0, len = m.length; i < len; i++){
7663             m[i].reject();
7664         }
7665     },
7666
7667     onMetaChange : function(meta, rtype, o){
7668         this.recordType = rtype;
7669         this.fields = rtype.prototype.fields;
7670         delete this.snapshot;
7671         this.sortInfo = meta.sortInfo || this.sortInfo;
7672         this.modified = [];
7673         this.fireEvent('metachange', this, this.reader.meta);
7674     },
7675     
7676     moveIndex : function(data, type)
7677     {
7678         var index = this.indexOf(data);
7679         
7680         var newIndex = index + type;
7681         
7682         this.remove(data);
7683         
7684         this.insert(newIndex, data);
7685         
7686     }
7687 });/*
7688  * Based on:
7689  * Ext JS Library 1.1.1
7690  * Copyright(c) 2006-2007, Ext JS, LLC.
7691  *
7692  * Originally Released Under LGPL - original licence link has changed is not relivant.
7693  *
7694  * Fork - LGPL
7695  * <script type="text/javascript">
7696  */
7697
7698 /**
7699  * @class Roo.data.SimpleStore
7700  * @extends Roo.data.Store
7701  * Small helper class to make creating Stores from Array data easier.
7702  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7703  * @cfg {Array} fields An array of field definition objects, or field name strings.
7704  * @cfg {Array} data The multi-dimensional array of data
7705  * @constructor
7706  * @param {Object} config
7707  */
7708 Roo.data.SimpleStore = function(config){
7709     Roo.data.SimpleStore.superclass.constructor.call(this, {
7710         isLocal : true,
7711         reader: new Roo.data.ArrayReader({
7712                 id: config.id
7713             },
7714             Roo.data.Record.create(config.fields)
7715         ),
7716         proxy : new Roo.data.MemoryProxy(config.data)
7717     });
7718     this.load();
7719 };
7720 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7721  * Based on:
7722  * Ext JS Library 1.1.1
7723  * Copyright(c) 2006-2007, Ext JS, LLC.
7724  *
7725  * Originally Released Under LGPL - original licence link has changed is not relivant.
7726  *
7727  * Fork - LGPL
7728  * <script type="text/javascript">
7729  */
7730
7731 /**
7732 /**
7733  * @extends Roo.data.Store
7734  * @class Roo.data.JsonStore
7735  * Small helper class to make creating Stores for JSON data easier. <br/>
7736 <pre><code>
7737 var store = new Roo.data.JsonStore({
7738     url: 'get-images.php',
7739     root: 'images',
7740     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
7741 });
7742 </code></pre>
7743  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
7744  * JsonReader and HttpProxy (unless inline data is provided).</b>
7745  * @cfg {Array} fields An array of field definition objects, or field name strings.
7746  * @constructor
7747  * @param {Object} config
7748  */
7749 Roo.data.JsonStore = function(c){
7750     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
7751         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
7752         reader: new Roo.data.JsonReader(c, c.fields)
7753     }));
7754 };
7755 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
7756  * Based on:
7757  * Ext JS Library 1.1.1
7758  * Copyright(c) 2006-2007, Ext JS, LLC.
7759  *
7760  * Originally Released Under LGPL - original licence link has changed is not relivant.
7761  *
7762  * Fork - LGPL
7763  * <script type="text/javascript">
7764  */
7765
7766  
7767 Roo.data.Field = function(config){
7768     if(typeof config == "string"){
7769         config = {name: config};
7770     }
7771     Roo.apply(this, config);
7772     
7773     if(!this.type){
7774         this.type = "auto";
7775     }
7776     
7777     var st = Roo.data.SortTypes;
7778     // named sortTypes are supported, here we look them up
7779     if(typeof this.sortType == "string"){
7780         this.sortType = st[this.sortType];
7781     }
7782     
7783     // set default sortType for strings and dates
7784     if(!this.sortType){
7785         switch(this.type){
7786             case "string":
7787                 this.sortType = st.asUCString;
7788                 break;
7789             case "date":
7790                 this.sortType = st.asDate;
7791                 break;
7792             default:
7793                 this.sortType = st.none;
7794         }
7795     }
7796
7797     // define once
7798     var stripRe = /[\$,%]/g;
7799
7800     // prebuilt conversion function for this field, instead of
7801     // switching every time we're reading a value
7802     if(!this.convert){
7803         var cv, dateFormat = this.dateFormat;
7804         switch(this.type){
7805             case "":
7806             case "auto":
7807             case undefined:
7808                 cv = function(v){ return v; };
7809                 break;
7810             case "string":
7811                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7812                 break;
7813             case "int":
7814                 cv = function(v){
7815                     return v !== undefined && v !== null && v !== '' ?
7816                            parseInt(String(v).replace(stripRe, ""), 10) : '';
7817                     };
7818                 break;
7819             case "float":
7820                 cv = function(v){
7821                     return v !== undefined && v !== null && v !== '' ?
7822                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
7823                     };
7824                 break;
7825             case "bool":
7826             case "boolean":
7827                 cv = function(v){ return v === true || v === "true" || v == 1; };
7828                 break;
7829             case "date":
7830                 cv = function(v){
7831                     if(!v){
7832                         return '';
7833                     }
7834                     if(v instanceof Date){
7835                         return v;
7836                     }
7837                     if(dateFormat){
7838                         if(dateFormat == "timestamp"){
7839                             return new Date(v*1000);
7840                         }
7841                         return Date.parseDate(v, dateFormat);
7842                     }
7843                     var parsed = Date.parse(v);
7844                     return parsed ? new Date(parsed) : null;
7845                 };
7846              break;
7847             
7848         }
7849         this.convert = cv;
7850     }
7851 };
7852
7853 Roo.data.Field.prototype = {
7854     dateFormat: null,
7855     defaultValue: "",
7856     mapping: null,
7857     sortType : null,
7858     sortDir : "ASC"
7859 };/*
7860  * Based on:
7861  * Ext JS Library 1.1.1
7862  * Copyright(c) 2006-2007, Ext JS, LLC.
7863  *
7864  * Originally Released Under LGPL - original licence link has changed is not relivant.
7865  *
7866  * Fork - LGPL
7867  * <script type="text/javascript">
7868  */
7869  
7870 // Base class for reading structured data from a data source.  This class is intended to be
7871 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7872
7873 /**
7874  * @class Roo.data.DataReader
7875  * Base class for reading structured data from a data source.  This class is intended to be
7876  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7877  */
7878
7879 Roo.data.DataReader = function(meta, recordType){
7880     
7881     this.meta = meta;
7882     
7883     this.recordType = recordType instanceof Array ? 
7884         Roo.data.Record.create(recordType) : recordType;
7885 };
7886
7887 Roo.data.DataReader.prototype = {
7888      /**
7889      * Create an empty record
7890      * @param {Object} data (optional) - overlay some values
7891      * @return {Roo.data.Record} record created.
7892      */
7893     newRow :  function(d) {
7894         var da =  {};
7895         this.recordType.prototype.fields.each(function(c) {
7896             switch( c.type) {
7897                 case 'int' : da[c.name] = 0; break;
7898                 case 'date' : da[c.name] = new Date(); break;
7899                 case 'float' : da[c.name] = 0.0; break;
7900                 case 'boolean' : da[c.name] = false; break;
7901                 default : da[c.name] = ""; break;
7902             }
7903             
7904         });
7905         return new this.recordType(Roo.apply(da, d));
7906     }
7907     
7908 };/*
7909  * Based on:
7910  * Ext JS Library 1.1.1
7911  * Copyright(c) 2006-2007, Ext JS, LLC.
7912  *
7913  * Originally Released Under LGPL - original licence link has changed is not relivant.
7914  *
7915  * Fork - LGPL
7916  * <script type="text/javascript">
7917  */
7918
7919 /**
7920  * @class Roo.data.DataProxy
7921  * @extends Roo.data.Observable
7922  * This class is an abstract base class for implementations which provide retrieval of
7923  * unformatted data objects.<br>
7924  * <p>
7925  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7926  * (of the appropriate type which knows how to parse the data object) to provide a block of
7927  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7928  * <p>
7929  * Custom implementations must implement the load method as described in
7930  * {@link Roo.data.HttpProxy#load}.
7931  */
7932 Roo.data.DataProxy = function(){
7933     this.addEvents({
7934         /**
7935          * @event beforeload
7936          * Fires before a network request is made to retrieve a data object.
7937          * @param {Object} This DataProxy object.
7938          * @param {Object} params The params parameter to the load function.
7939          */
7940         beforeload : true,
7941         /**
7942          * @event load
7943          * Fires before the load method's callback is called.
7944          * @param {Object} This DataProxy object.
7945          * @param {Object} o The data object.
7946          * @param {Object} arg The callback argument object passed to the load function.
7947          */
7948         load : true,
7949         /**
7950          * @event loadexception
7951          * Fires if an Exception occurs during data retrieval.
7952          * @param {Object} This DataProxy object.
7953          * @param {Object} o The data object.
7954          * @param {Object} arg The callback argument object passed to the load function.
7955          * @param {Object} e The Exception.
7956          */
7957         loadexception : true
7958     });
7959     Roo.data.DataProxy.superclass.constructor.call(this);
7960 };
7961
7962 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7963
7964     /**
7965      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7966      */
7967 /*
7968  * Based on:
7969  * Ext JS Library 1.1.1
7970  * Copyright(c) 2006-2007, Ext JS, LLC.
7971  *
7972  * Originally Released Under LGPL - original licence link has changed is not relivant.
7973  *
7974  * Fork - LGPL
7975  * <script type="text/javascript">
7976  */
7977 /**
7978  * @class Roo.data.MemoryProxy
7979  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7980  * to the Reader when its load method is called.
7981  * @constructor
7982  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7983  */
7984 Roo.data.MemoryProxy = function(data){
7985     if (data.data) {
7986         data = data.data;
7987     }
7988     Roo.data.MemoryProxy.superclass.constructor.call(this);
7989     this.data = data;
7990 };
7991
7992 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7993     /**
7994      * Load data from the requested source (in this case an in-memory
7995      * data object passed to the constructor), read the data object into
7996      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7997      * process that block using the passed callback.
7998      * @param {Object} params This parameter is not used by the MemoryProxy class.
7999      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8000      * object into a block of Roo.data.Records.
8001      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8002      * The function must be passed <ul>
8003      * <li>The Record block object</li>
8004      * <li>The "arg" argument from the load function</li>
8005      * <li>A boolean success indicator</li>
8006      * </ul>
8007      * @param {Object} scope The scope in which to call the callback
8008      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8009      */
8010     load : function(params, reader, callback, scope, arg){
8011         params = params || {};
8012         var result;
8013         try {
8014             result = reader.readRecords(this.data);
8015         }catch(e){
8016             this.fireEvent("loadexception", this, arg, null, e);
8017             callback.call(scope, null, arg, false);
8018             return;
8019         }
8020         callback.call(scope, result, arg, true);
8021     },
8022     
8023     // private
8024     update : function(params, records){
8025         
8026     }
8027 });/*
8028  * Based on:
8029  * Ext JS Library 1.1.1
8030  * Copyright(c) 2006-2007, Ext JS, LLC.
8031  *
8032  * Originally Released Under LGPL - original licence link has changed is not relivant.
8033  *
8034  * Fork - LGPL
8035  * <script type="text/javascript">
8036  */
8037 /**
8038  * @class Roo.data.HttpProxy
8039  * @extends Roo.data.DataProxy
8040  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8041  * configured to reference a certain URL.<br><br>
8042  * <p>
8043  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8044  * from which the running page was served.<br><br>
8045  * <p>
8046  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8047  * <p>
8048  * Be aware that to enable the browser to parse an XML document, the server must set
8049  * the Content-Type header in the HTTP response to "text/xml".
8050  * @constructor
8051  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8052  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8053  * will be used to make the request.
8054  */
8055 Roo.data.HttpProxy = function(conn){
8056     Roo.data.HttpProxy.superclass.constructor.call(this);
8057     // is conn a conn config or a real conn?
8058     this.conn = conn;
8059     this.useAjax = !conn || !conn.events;
8060   
8061 };
8062
8063 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8064     // thse are take from connection...
8065     
8066     /**
8067      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8068      */
8069     /**
8070      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8071      * extra parameters to each request made by this object. (defaults to undefined)
8072      */
8073     /**
8074      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8075      *  to each request made by this object. (defaults to undefined)
8076      */
8077     /**
8078      * @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)
8079      */
8080     /**
8081      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8082      */
8083      /**
8084      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8085      * @type Boolean
8086      */
8087   
8088
8089     /**
8090      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8091      * @type Boolean
8092      */
8093     /**
8094      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8095      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8096      * a finer-grained basis than the DataProxy events.
8097      */
8098     getConnection : function(){
8099         return this.useAjax ? Roo.Ajax : this.conn;
8100     },
8101
8102     /**
8103      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8104      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8105      * process that block using the passed callback.
8106      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8107      * for the request to the remote server.
8108      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8109      * object into a block of Roo.data.Records.
8110      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8111      * The function must be passed <ul>
8112      * <li>The Record block object</li>
8113      * <li>The "arg" argument from the load function</li>
8114      * <li>A boolean success indicator</li>
8115      * </ul>
8116      * @param {Object} scope The scope in which to call the callback
8117      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8118      */
8119     load : function(params, reader, callback, scope, arg){
8120         if(this.fireEvent("beforeload", this, params) !== false){
8121             var  o = {
8122                 params : params || {},
8123                 request: {
8124                     callback : callback,
8125                     scope : scope,
8126                     arg : arg
8127                 },
8128                 reader: reader,
8129                 callback : this.loadResponse,
8130                 scope: this
8131             };
8132             if(this.useAjax){
8133                 Roo.applyIf(o, this.conn);
8134                 if(this.activeRequest){
8135                     Roo.Ajax.abort(this.activeRequest);
8136                 }
8137                 this.activeRequest = Roo.Ajax.request(o);
8138             }else{
8139                 this.conn.request(o);
8140             }
8141         }else{
8142             callback.call(scope||this, null, arg, false);
8143         }
8144     },
8145
8146     // private
8147     loadResponse : function(o, success, response){
8148         delete this.activeRequest;
8149         if(!success){
8150             this.fireEvent("loadexception", this, o, response);
8151             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8152             return;
8153         }
8154         var result;
8155         try {
8156             result = o.reader.read(response);
8157         }catch(e){
8158             this.fireEvent("loadexception", this, o, response, e);
8159             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8160             return;
8161         }
8162         
8163         this.fireEvent("load", this, o, o.request.arg);
8164         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8165     },
8166
8167     // private
8168     update : function(dataSet){
8169
8170     },
8171
8172     // private
8173     updateResponse : function(dataSet){
8174
8175     }
8176 });/*
8177  * Based on:
8178  * Ext JS Library 1.1.1
8179  * Copyright(c) 2006-2007, Ext JS, LLC.
8180  *
8181  * Originally Released Under LGPL - original licence link has changed is not relivant.
8182  *
8183  * Fork - LGPL
8184  * <script type="text/javascript">
8185  */
8186
8187 /**
8188  * @class Roo.data.ScriptTagProxy
8189  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8190  * other than the originating domain of the running page.<br><br>
8191  * <p>
8192  * <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
8193  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8194  * <p>
8195  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8196  * source code that is used as the source inside a &lt;script> tag.<br><br>
8197  * <p>
8198  * In order for the browser to process the returned data, the server must wrap the data object
8199  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8200  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8201  * depending on whether the callback name was passed:
8202  * <p>
8203  * <pre><code>
8204 boolean scriptTag = false;
8205 String cb = request.getParameter("callback");
8206 if (cb != null) {
8207     scriptTag = true;
8208     response.setContentType("text/javascript");
8209 } else {
8210     response.setContentType("application/x-json");
8211 }
8212 Writer out = response.getWriter();
8213 if (scriptTag) {
8214     out.write(cb + "(");
8215 }
8216 out.print(dataBlock.toJsonString());
8217 if (scriptTag) {
8218     out.write(");");
8219 }
8220 </pre></code>
8221  *
8222  * @constructor
8223  * @param {Object} config A configuration object.
8224  */
8225 Roo.data.ScriptTagProxy = function(config){
8226     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8227     Roo.apply(this, config);
8228     this.head = document.getElementsByTagName("head")[0];
8229 };
8230
8231 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8232
8233 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8234     /**
8235      * @cfg {String} url The URL from which to request the data object.
8236      */
8237     /**
8238      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8239      */
8240     timeout : 30000,
8241     /**
8242      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8243      * the server the name of the callback function set up by the load call to process the returned data object.
8244      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8245      * javascript output which calls this named function passing the data object as its only parameter.
8246      */
8247     callbackParam : "callback",
8248     /**
8249      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8250      * name to the request.
8251      */
8252     nocache : true,
8253
8254     /**
8255      * Load data from the configured URL, read the data object into
8256      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8257      * process that block using the passed callback.
8258      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8259      * for the request to the remote server.
8260      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8261      * object into a block of Roo.data.Records.
8262      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8263      * The function must be passed <ul>
8264      * <li>The Record block object</li>
8265      * <li>The "arg" argument from the load function</li>
8266      * <li>A boolean success indicator</li>
8267      * </ul>
8268      * @param {Object} scope The scope in which to call the callback
8269      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8270      */
8271     load : function(params, reader, callback, scope, arg){
8272         if(this.fireEvent("beforeload", this, params) !== false){
8273
8274             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8275
8276             var url = this.url;
8277             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8278             if(this.nocache){
8279                 url += "&_dc=" + (new Date().getTime());
8280             }
8281             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8282             var trans = {
8283                 id : transId,
8284                 cb : "stcCallback"+transId,
8285                 scriptId : "stcScript"+transId,
8286                 params : params,
8287                 arg : arg,
8288                 url : url,
8289                 callback : callback,
8290                 scope : scope,
8291                 reader : reader
8292             };
8293             var conn = this;
8294
8295             window[trans.cb] = function(o){
8296                 conn.handleResponse(o, trans);
8297             };
8298
8299             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8300
8301             if(this.autoAbort !== false){
8302                 this.abort();
8303             }
8304
8305             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8306
8307             var script = document.createElement("script");
8308             script.setAttribute("src", url);
8309             script.setAttribute("type", "text/javascript");
8310             script.setAttribute("id", trans.scriptId);
8311             this.head.appendChild(script);
8312
8313             this.trans = trans;
8314         }else{
8315             callback.call(scope||this, null, arg, false);
8316         }
8317     },
8318
8319     // private
8320     isLoading : function(){
8321         return this.trans ? true : false;
8322     },
8323
8324     /**
8325      * Abort the current server request.
8326      */
8327     abort : function(){
8328         if(this.isLoading()){
8329             this.destroyTrans(this.trans);
8330         }
8331     },
8332
8333     // private
8334     destroyTrans : function(trans, isLoaded){
8335         this.head.removeChild(document.getElementById(trans.scriptId));
8336         clearTimeout(trans.timeoutId);
8337         if(isLoaded){
8338             window[trans.cb] = undefined;
8339             try{
8340                 delete window[trans.cb];
8341             }catch(e){}
8342         }else{
8343             // if hasn't been loaded, wait for load to remove it to prevent script error
8344             window[trans.cb] = function(){
8345                 window[trans.cb] = undefined;
8346                 try{
8347                     delete window[trans.cb];
8348                 }catch(e){}
8349             };
8350         }
8351     },
8352
8353     // private
8354     handleResponse : function(o, trans){
8355         this.trans = false;
8356         this.destroyTrans(trans, true);
8357         var result;
8358         try {
8359             result = trans.reader.readRecords(o);
8360         }catch(e){
8361             this.fireEvent("loadexception", this, o, trans.arg, e);
8362             trans.callback.call(trans.scope||window, null, trans.arg, false);
8363             return;
8364         }
8365         this.fireEvent("load", this, o, trans.arg);
8366         trans.callback.call(trans.scope||window, result, trans.arg, true);
8367     },
8368
8369     // private
8370     handleFailure : function(trans){
8371         this.trans = false;
8372         this.destroyTrans(trans, false);
8373         this.fireEvent("loadexception", this, null, trans.arg);
8374         trans.callback.call(trans.scope||window, null, trans.arg, false);
8375     }
8376 });/*
8377  * Based on:
8378  * Ext JS Library 1.1.1
8379  * Copyright(c) 2006-2007, Ext JS, LLC.
8380  *
8381  * Originally Released Under LGPL - original licence link has changed is not relivant.
8382  *
8383  * Fork - LGPL
8384  * <script type="text/javascript">
8385  */
8386
8387 /**
8388  * @class Roo.data.JsonReader
8389  * @extends Roo.data.DataReader
8390  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8391  * based on mappings in a provided Roo.data.Record constructor.
8392  * 
8393  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8394  * in the reply previously. 
8395  * 
8396  * <p>
8397  * Example code:
8398  * <pre><code>
8399 var RecordDef = Roo.data.Record.create([
8400     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8401     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8402 ]);
8403 var myReader = new Roo.data.JsonReader({
8404     totalProperty: "results",    // The property which contains the total dataset size (optional)
8405     root: "rows",                // The property which contains an Array of row objects
8406     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8407 }, RecordDef);
8408 </code></pre>
8409  * <p>
8410  * This would consume a JSON file like this:
8411  * <pre><code>
8412 { 'results': 2, 'rows': [
8413     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8414     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8415 }
8416 </code></pre>
8417  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8418  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8419  * paged from the remote server.
8420  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8421  * @cfg {String} root name of the property which contains the Array of row objects.
8422  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8423  * @constructor
8424  * Create a new JsonReader
8425  * @param {Object} meta Metadata configuration options
8426  * @param {Object} recordType Either an Array of field definition objects,
8427  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8428  */
8429 Roo.data.JsonReader = function(meta, recordType){
8430     
8431     meta = meta || {};
8432     // set some defaults:
8433     Roo.applyIf(meta, {
8434         totalProperty: 'total',
8435         successProperty : 'success',
8436         root : 'data',
8437         id : 'id'
8438     });
8439     
8440     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8441 };
8442 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8443     
8444     /**
8445      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8446      * Used by Store query builder to append _requestMeta to params.
8447      * 
8448      */
8449     metaFromRemote : false,
8450     /**
8451      * This method is only used by a DataProxy which has retrieved data from a remote server.
8452      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8453      * @return {Object} data A data block which is used by an Roo.data.Store object as
8454      * a cache of Roo.data.Records.
8455      */
8456     read : function(response){
8457         var json = response.responseText;
8458        
8459         var o = /* eval:var:o */ eval("("+json+")");
8460         if(!o) {
8461             throw {message: "JsonReader.read: Json object not found"};
8462         }
8463         
8464         if(o.metaData){
8465             
8466             delete this.ef;
8467             this.metaFromRemote = true;
8468             this.meta = o.metaData;
8469             this.recordType = Roo.data.Record.create(o.metaData.fields);
8470             this.onMetaChange(this.meta, this.recordType, o);
8471         }
8472         return this.readRecords(o);
8473     },
8474
8475     // private function a store will implement
8476     onMetaChange : function(meta, recordType, o){
8477
8478     },
8479
8480     /**
8481          * @ignore
8482          */
8483     simpleAccess: function(obj, subsc) {
8484         return obj[subsc];
8485     },
8486
8487         /**
8488          * @ignore
8489          */
8490     getJsonAccessor: function(){
8491         var re = /[\[\.]/;
8492         return function(expr) {
8493             try {
8494                 return(re.test(expr))
8495                     ? new Function("obj", "return obj." + expr)
8496                     : function(obj){
8497                         return obj[expr];
8498                     };
8499             } catch(e){}
8500             return Roo.emptyFn;
8501         };
8502     }(),
8503
8504     /**
8505      * Create a data block containing Roo.data.Records from an XML document.
8506      * @param {Object} o An object which contains an Array of row objects in the property specified
8507      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8508      * which contains the total size of the dataset.
8509      * @return {Object} data A data block which is used by an Roo.data.Store object as
8510      * a cache of Roo.data.Records.
8511      */
8512     readRecords : function(o){
8513         /**
8514          * After any data loads, the raw JSON data is available for further custom processing.
8515          * @type Object
8516          */
8517         this.o = o;
8518         var s = this.meta, Record = this.recordType,
8519             f = Record.prototype.fields, fi = f.items, fl = f.length;
8520
8521 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8522         if (!this.ef) {
8523             if(s.totalProperty) {
8524                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8525                 }
8526                 if(s.successProperty) {
8527                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8528                 }
8529                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8530                 if (s.id) {
8531                         var g = this.getJsonAccessor(s.id);
8532                         this.getId = function(rec) {
8533                                 var r = g(rec);
8534                                 return (r === undefined || r === "") ? null : r;
8535                         };
8536                 } else {
8537                         this.getId = function(){return null;};
8538                 }
8539             this.ef = [];
8540             for(var jj = 0; jj < fl; jj++){
8541                 f = fi[jj];
8542                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8543                 this.ef[jj] = this.getJsonAccessor(map);
8544             }
8545         }
8546
8547         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8548         if(s.totalProperty){
8549             var vt = parseInt(this.getTotal(o), 10);
8550             if(!isNaN(vt)){
8551                 totalRecords = vt;
8552             }
8553         }
8554         if(s.successProperty){
8555             var vs = this.getSuccess(o);
8556             if(vs === false || vs === 'false'){
8557                 success = false;
8558             }
8559         }
8560         var records = [];
8561             for(var i = 0; i < c; i++){
8562                     var n = root[i];
8563                 var values = {};
8564                 var id = this.getId(n);
8565                 for(var j = 0; j < fl; j++){
8566                     f = fi[j];
8567                 var v = this.ef[j](n);
8568                 if (!f.convert) {
8569                     Roo.log('missing convert for ' + f.name);
8570                     Roo.log(f);
8571                     continue;
8572                 }
8573                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8574                 }
8575                 var record = new Record(values, id);
8576                 record.json = n;
8577                 records[i] = record;
8578             }
8579             return {
8580             raw : o,
8581                 success : success,
8582                 records : records,
8583                 totalRecords : totalRecords
8584             };
8585     }
8586 });/*
8587  * Based on:
8588  * Ext JS Library 1.1.1
8589  * Copyright(c) 2006-2007, Ext JS, LLC.
8590  *
8591  * Originally Released Under LGPL - original licence link has changed is not relivant.
8592  *
8593  * Fork - LGPL
8594  * <script type="text/javascript">
8595  */
8596
8597 /**
8598  * @class Roo.data.ArrayReader
8599  * @extends Roo.data.DataReader
8600  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8601  * Each element of that Array represents a row of data fields. The
8602  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8603  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8604  * <p>
8605  * Example code:.
8606  * <pre><code>
8607 var RecordDef = Roo.data.Record.create([
8608     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8609     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8610 ]);
8611 var myReader = new Roo.data.ArrayReader({
8612     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8613 }, RecordDef);
8614 </code></pre>
8615  * <p>
8616  * This would consume an Array like this:
8617  * <pre><code>
8618 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8619   </code></pre>
8620  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8621  * @constructor
8622  * Create a new JsonReader
8623  * @param {Object} meta Metadata configuration options.
8624  * @param {Object} recordType Either an Array of field definition objects
8625  * as specified to {@link Roo.data.Record#create},
8626  * or an {@link Roo.data.Record} object
8627  * created using {@link Roo.data.Record#create}.
8628  */
8629 Roo.data.ArrayReader = function(meta, recordType){
8630     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8631 };
8632
8633 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8634     /**
8635      * Create a data block containing Roo.data.Records from an XML document.
8636      * @param {Object} o An Array of row objects which represents the dataset.
8637      * @return {Object} data A data block which is used by an Roo.data.Store object as
8638      * a cache of Roo.data.Records.
8639      */
8640     readRecords : function(o){
8641         var sid = this.meta ? this.meta.id : null;
8642         var recordType = this.recordType, fields = recordType.prototype.fields;
8643         var records = [];
8644         var root = o;
8645             for(var i = 0; i < root.length; i++){
8646                     var n = root[i];
8647                 var values = {};
8648                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8649                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8650                 var f = fields.items[j];
8651                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8652                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8653                 v = f.convert(v);
8654                 values[f.name] = v;
8655             }
8656                 var record = new recordType(values, id);
8657                 record.json = n;
8658                 records[records.length] = record;
8659             }
8660             return {
8661                 records : records,
8662                 totalRecords : records.length
8663             };
8664     }
8665 });/*
8666  * - LGPL
8667  * * 
8668  */
8669
8670 /**
8671  * @class Roo.bootstrap.ComboBox
8672  * @extends Roo.bootstrap.TriggerField
8673  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8674  * @cfg {Boolean} append (true|false) default false
8675  * @constructor
8676  * Create a new ComboBox.
8677  * @param {Object} config Configuration options
8678  */
8679 Roo.bootstrap.ComboBox = function(config){
8680     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8681     this.addEvents({
8682         /**
8683          * @event expand
8684          * Fires when the dropdown list is expanded
8685              * @param {Roo.bootstrap.ComboBox} combo This combo box
8686              */
8687         'expand' : true,
8688         /**
8689          * @event collapse
8690          * Fires when the dropdown list is collapsed
8691              * @param {Roo.bootstrap.ComboBox} combo This combo box
8692              */
8693         'collapse' : true,
8694         /**
8695          * @event beforeselect
8696          * Fires before a list item is selected. Return false to cancel the selection.
8697              * @param {Roo.bootstrap.ComboBox} combo This combo box
8698              * @param {Roo.data.Record} record The data record returned from the underlying store
8699              * @param {Number} index The index of the selected item in the dropdown list
8700              */
8701         'beforeselect' : true,
8702         /**
8703          * @event select
8704          * Fires when a list item is selected
8705              * @param {Roo.bootstrap.ComboBox} combo This combo box
8706              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8707              * @param {Number} index The index of the selected item in the dropdown list
8708              */
8709         'select' : true,
8710         /**
8711          * @event beforequery
8712          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8713          * The event object passed has these properties:
8714              * @param {Roo.bootstrap.ComboBox} combo This combo box
8715              * @param {String} query The query
8716              * @param {Boolean} forceAll true to force "all" query
8717              * @param {Boolean} cancel true to cancel the query
8718              * @param {Object} e The query event object
8719              */
8720         'beforequery': true,
8721          /**
8722          * @event add
8723          * Fires when the 'add' icon is pressed (add a listener to enable add button)
8724              * @param {Roo.bootstrap.ComboBox} combo This combo box
8725              */
8726         'add' : true,
8727         /**
8728          * @event edit
8729          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
8730              * @param {Roo.bootstrap.ComboBox} combo This combo box
8731              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
8732              */
8733         'edit' : true,
8734         /**
8735          * @event remove
8736          * Fires when the remove value from the combobox array
8737              * @param {Roo.bootstrap.ComboBox} combo This combo box
8738              */
8739         'remove' : true
8740         
8741     });
8742     
8743     
8744     this.selectedIndex = -1;
8745     if(this.mode == 'local'){
8746         if(config.queryDelay === undefined){
8747             this.queryDelay = 10;
8748         }
8749         if(config.minChars === undefined){
8750             this.minChars = 0;
8751         }
8752     }
8753 };
8754
8755 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
8756      
8757     /**
8758      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
8759      * rendering into an Roo.Editor, defaults to false)
8760      */
8761     /**
8762      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
8763      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
8764      */
8765     /**
8766      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
8767      */
8768     /**
8769      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
8770      * the dropdown list (defaults to undefined, with no header element)
8771      */
8772
8773      /**
8774      * @cfg {String/Roo.Template} tpl The template to use to render the output
8775      */
8776      
8777      /**
8778      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
8779      */
8780     listWidth: undefined,
8781     /**
8782      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8783      * mode = 'remote' or 'text' if mode = 'local')
8784      */
8785     displayField: undefined,
8786     /**
8787      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8788      * mode = 'remote' or 'value' if mode = 'local'). 
8789      * Note: use of a valueField requires the user make a selection
8790      * in order for a value to be mapped.
8791      */
8792     valueField: undefined,
8793     
8794     
8795     /**
8796      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8797      * field's data value (defaults to the underlying DOM element's name)
8798      */
8799     hiddenName: undefined,
8800     /**
8801      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8802      */
8803     listClass: '',
8804     /**
8805      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8806      */
8807     selectedClass: 'active',
8808     
8809     /**
8810      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8811      */
8812     shadow:'sides',
8813     /**
8814      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8815      * anchor positions (defaults to 'tl-bl')
8816      */
8817     listAlign: 'tl-bl?',
8818     /**
8819      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8820      */
8821     maxHeight: 300,
8822     /**
8823      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
8824      * query specified by the allQuery config option (defaults to 'query')
8825      */
8826     triggerAction: 'query',
8827     /**
8828      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8829      * (defaults to 4, does not apply if editable = false)
8830      */
8831     minChars : 4,
8832     /**
8833      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8834      * delay (typeAheadDelay) if it matches a known value (defaults to false)
8835      */
8836     typeAhead: false,
8837     /**
8838      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8839      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8840      */
8841     queryDelay: 500,
8842     /**
8843      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8844      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
8845      */
8846     pageSize: 0,
8847     /**
8848      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
8849      * when editable = true (defaults to false)
8850      */
8851     selectOnFocus:false,
8852     /**
8853      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8854      */
8855     queryParam: 'query',
8856     /**
8857      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
8858      * when mode = 'remote' (defaults to 'Loading...')
8859      */
8860     loadingText: 'Loading...',
8861     /**
8862      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8863      */
8864     resizable: false,
8865     /**
8866      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8867      */
8868     handleHeight : 8,
8869     /**
8870      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8871      * traditional select (defaults to true)
8872      */
8873     editable: true,
8874     /**
8875      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8876      */
8877     allQuery: '',
8878     /**
8879      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8880      */
8881     mode: 'remote',
8882     /**
8883      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8884      * listWidth has a higher value)
8885      */
8886     minListWidth : 70,
8887     /**
8888      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8889      * allow the user to set arbitrary text into the field (defaults to false)
8890      */
8891     forceSelection:false,
8892     /**
8893      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8894      * if typeAhead = true (defaults to 250)
8895      */
8896     typeAheadDelay : 250,
8897     /**
8898      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8899      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8900      */
8901     valueNotFoundText : undefined,
8902     /**
8903      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8904      */
8905     blockFocus : false,
8906     
8907     /**
8908      * @cfg {Boolean} disableClear Disable showing of clear button.
8909      */
8910     disableClear : false,
8911     /**
8912      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8913      */
8914     alwaysQuery : false,
8915     
8916     /**
8917      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8918      */
8919     multiple : false,
8920     
8921     //private
8922     addicon : false,
8923     editicon: false,
8924     
8925     page: 0,
8926     hasQuery: false,
8927     append: false,
8928     loadNext: false,
8929     item: [],
8930     
8931     // element that contains real text value.. (when hidden is used..)
8932      
8933     // private
8934     initEvents: function(){
8935         
8936         if (!this.store) {
8937             throw "can not find store for combo";
8938         }
8939         this.store = Roo.factory(this.store, Roo.data);
8940         
8941         
8942         
8943         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8944         
8945         
8946         if(this.hiddenName){
8947             
8948             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8949             
8950             this.hiddenField.dom.value =
8951                 this.hiddenValue !== undefined ? this.hiddenValue :
8952                 this.value !== undefined ? this.value : '';
8953
8954             // prevent input submission
8955             this.el.dom.removeAttribute('name');
8956             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8957              
8958              
8959         }
8960         //if(Roo.isGecko){
8961         //    this.el.dom.setAttribute('autocomplete', 'off');
8962         //}
8963
8964         var cls = 'x-combo-list';
8965         this.list = this.el.select('ul.dropdown-menu',true).first();
8966
8967         //this.list = new Roo.Layer({
8968         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8969         //});
8970         
8971         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8972         this.list.setWidth(lw);
8973         
8974         this.list.on('mouseover', this.onViewOver, this);
8975         this.list.on('mousemove', this.onViewMove, this);
8976         
8977         this.list.on('scroll', this.onViewScroll, this);
8978         
8979         /*
8980         this.list.swallowEvent('mousewheel');
8981         this.assetHeight = 0;
8982
8983         if(this.title){
8984             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8985             this.assetHeight += this.header.getHeight();
8986         }
8987
8988         this.innerList = this.list.createChild({cls:cls+'-inner'});
8989         this.innerList.on('mouseover', this.onViewOver, this);
8990         this.innerList.on('mousemove', this.onViewMove, this);
8991         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8992         
8993         if(this.allowBlank && !this.pageSize && !this.disableClear){
8994             this.footer = this.list.createChild({cls:cls+'-ft'});
8995             this.pageTb = new Roo.Toolbar(this.footer);
8996            
8997         }
8998         if(this.pageSize){
8999             this.footer = this.list.createChild({cls:cls+'-ft'});
9000             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9001                     {pageSize: this.pageSize});
9002             
9003         }
9004         
9005         if (this.pageTb && this.allowBlank && !this.disableClear) {
9006             var _this = this;
9007             this.pageTb.add(new Roo.Toolbar.Fill(), {
9008                 cls: 'x-btn-icon x-btn-clear',
9009                 text: '&#160;',
9010                 handler: function()
9011                 {
9012                     _this.collapse();
9013                     _this.clearValue();
9014                     _this.onSelect(false, -1);
9015                 }
9016             });
9017         }
9018         if (this.footer) {
9019             this.assetHeight += this.footer.getHeight();
9020         }
9021         */
9022             
9023         if(!this.tpl){
9024             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9025         }
9026
9027         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9028             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9029         });
9030         //this.view.wrapEl.setDisplayed(false);
9031         this.view.on('click', this.onViewClick, this);
9032         
9033         
9034         
9035         this.store.on('beforeload', this.onBeforeLoad, this);
9036         this.store.on('load', this.onLoad, this);
9037         this.store.on('loadexception', this.onLoadException, this);
9038         /*
9039         if(this.resizable){
9040             this.resizer = new Roo.Resizable(this.list,  {
9041                pinned:true, handles:'se'
9042             });
9043             this.resizer.on('resize', function(r, w, h){
9044                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9045                 this.listWidth = w;
9046                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9047                 this.restrictHeight();
9048             }, this);
9049             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9050         }
9051         */
9052         if(!this.editable){
9053             this.editable = true;
9054             this.setEditable(false);
9055         }
9056         
9057         /*
9058         
9059         if (typeof(this.events.add.listeners) != 'undefined') {
9060             
9061             this.addicon = this.wrap.createChild(
9062                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9063        
9064             this.addicon.on('click', function(e) {
9065                 this.fireEvent('add', this);
9066             }, this);
9067         }
9068         if (typeof(this.events.edit.listeners) != 'undefined') {
9069             
9070             this.editicon = this.wrap.createChild(
9071                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9072             if (this.addicon) {
9073                 this.editicon.setStyle('margin-left', '40px');
9074             }
9075             this.editicon.on('click', function(e) {
9076                 
9077                 // we fire even  if inothing is selected..
9078                 this.fireEvent('edit', this, this.lastData );
9079                 
9080             }, this);
9081         }
9082         */
9083         
9084         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9085             "up" : function(e){
9086                 this.inKeyMode = true;
9087                 this.selectPrev();
9088             },
9089
9090             "down" : function(e){
9091                 if(!this.isExpanded()){
9092                     this.onTriggerClick();
9093                 }else{
9094                     this.inKeyMode = true;
9095                     this.selectNext();
9096                 }
9097             },
9098
9099             "enter" : function(e){
9100                 this.onViewClick();
9101                 //return true;
9102             },
9103
9104             "esc" : function(e){
9105                 this.collapse();
9106             },
9107
9108             "tab" : function(e){
9109                 this.collapse();
9110                 
9111                 if(this.fireEvent("specialkey", this, e)){
9112                     this.onViewClick(false);
9113                 }
9114                 
9115                 return true;
9116             },
9117
9118             scope : this,
9119
9120             doRelay : function(foo, bar, hname){
9121                 if(hname == 'down' || this.scope.isExpanded()){
9122                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9123                 }
9124                 return true;
9125             },
9126
9127             forceKeyDown: true
9128         });
9129         
9130         
9131         this.queryDelay = Math.max(this.queryDelay || 10,
9132                 this.mode == 'local' ? 10 : 250);
9133         
9134         
9135         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9136         
9137         if(this.typeAhead){
9138             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9139         }
9140         if(this.editable !== false){
9141             this.inputEl().on("keyup", this.onKeyUp, this);
9142         }
9143         if(this.forceSelection){
9144             this.on('blur', this.doForce, this);
9145         }
9146         
9147         if(this.multiple){
9148             this.choices = this.el.select('ul.select2-choices', true).first();
9149             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9150         }
9151     },
9152
9153     onDestroy : function(){
9154         if(this.view){
9155             this.view.setStore(null);
9156             this.view.el.removeAllListeners();
9157             this.view.el.remove();
9158             this.view.purgeListeners();
9159         }
9160         if(this.list){
9161             this.list.dom.innerHTML  = '';
9162         }
9163         if(this.store){
9164             this.store.un('beforeload', this.onBeforeLoad, this);
9165             this.store.un('load', this.onLoad, this);
9166             this.store.un('loadexception', this.onLoadException, this);
9167         }
9168         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9169     },
9170
9171     // private
9172     fireKey : function(e){
9173         if(e.isNavKeyPress() && !this.list.isVisible()){
9174             this.fireEvent("specialkey", this, e);
9175         }
9176     },
9177
9178     // private
9179     onResize: function(w, h){
9180 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9181 //        
9182 //        if(typeof w != 'number'){
9183 //            // we do not handle it!?!?
9184 //            return;
9185 //        }
9186 //        var tw = this.trigger.getWidth();
9187 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9188 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9189 //        var x = w - tw;
9190 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9191 //            
9192 //        //this.trigger.setStyle('left', x+'px');
9193 //        
9194 //        if(this.list && this.listWidth === undefined){
9195 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9196 //            this.list.setWidth(lw);
9197 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9198 //        }
9199         
9200     
9201         
9202     },
9203
9204     /**
9205      * Allow or prevent the user from directly editing the field text.  If false is passed,
9206      * the user will only be able to select from the items defined in the dropdown list.  This method
9207      * is the runtime equivalent of setting the 'editable' config option at config time.
9208      * @param {Boolean} value True to allow the user to directly edit the field text
9209      */
9210     setEditable : function(value){
9211         if(value == this.editable){
9212             return;
9213         }
9214         this.editable = value;
9215         if(!value){
9216             this.inputEl().dom.setAttribute('readOnly', true);
9217             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9218             this.inputEl().addClass('x-combo-noedit');
9219         }else{
9220             this.inputEl().dom.setAttribute('readOnly', false);
9221             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9222             this.inputEl().removeClass('x-combo-noedit');
9223         }
9224     },
9225
9226     // private
9227     
9228     onBeforeLoad : function(combo,opts){
9229         if(!this.hasFocus){
9230             return;
9231         }
9232          if (!opts.add) {
9233             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9234          }
9235         this.restrictHeight();
9236         this.selectedIndex = -1;
9237     },
9238
9239     // private
9240     onLoad : function(){
9241         
9242         this.hasQuery = false;
9243         
9244         if(!this.hasFocus){
9245             return;
9246         }
9247         
9248         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9249             this.loading.hide();
9250         }
9251         
9252         if(this.store.getCount() > 0){
9253             this.expand();
9254             this.restrictHeight();
9255             if(this.lastQuery == this.allQuery){
9256                 if(this.editable){
9257                     this.inputEl().dom.select();
9258                 }
9259                 if(!this.selectByValue(this.value, true)){
9260                     this.select(0, true);
9261                 }
9262             }else{
9263                 this.selectNext();
9264                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9265                     this.taTask.delay(this.typeAheadDelay);
9266                 }
9267             }
9268         }else{
9269             this.onEmptyResults();
9270         }
9271         
9272         //this.el.focus();
9273     },
9274     // private
9275     onLoadException : function()
9276     {
9277         this.hasQuery = false;
9278         
9279         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9280             this.loading.hide();
9281         }
9282         
9283         this.collapse();
9284         Roo.log(this.store.reader.jsonData);
9285         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9286             // fixme
9287             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9288         }
9289         
9290         
9291     },
9292     // private
9293     onTypeAhead : function(){
9294         if(this.store.getCount() > 0){
9295             var r = this.store.getAt(0);
9296             var newValue = r.data[this.displayField];
9297             var len = newValue.length;
9298             var selStart = this.getRawValue().length;
9299             
9300             if(selStart != len){
9301                 this.setRawValue(newValue);
9302                 this.selectText(selStart, newValue.length);
9303             }
9304         }
9305     },
9306
9307     // private
9308     onSelect : function(record, index){
9309         
9310         if(this.fireEvent('beforeselect', this, record, index) !== false){
9311         
9312             this.setFromData(index > -1 ? record.data : false);
9313             
9314             this.collapse();
9315             this.fireEvent('select', this, record, index);
9316         }
9317     },
9318
9319     /**
9320      * Returns the currently selected field value or empty string if no value is set.
9321      * @return {String} value The selected value
9322      */
9323     getValue : function(){
9324         
9325         if(this.multiple){
9326             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9327         }
9328         
9329         if(this.valueField){
9330             return typeof this.value != 'undefined' ? this.value : '';
9331         }else{
9332             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9333         }
9334     },
9335
9336     /**
9337      * Clears any text/value currently set in the field
9338      */
9339     clearValue : function(){
9340         if(this.hiddenField){
9341             this.hiddenField.dom.value = '';
9342         }
9343         this.value = '';
9344         this.setRawValue('');
9345         this.lastSelectionText = '';
9346         
9347     },
9348
9349     /**
9350      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9351      * will be displayed in the field.  If the value does not match the data value of an existing item,
9352      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9353      * Otherwise the field will be blank (although the value will still be set).
9354      * @param {String} value The value to match
9355      */
9356     setValue : function(v){
9357         if(this.multiple){
9358             this.syncValue();
9359             return;
9360         }
9361         
9362         var text = v;
9363         if(this.valueField){
9364             var r = this.findRecord(this.valueField, v);
9365             if(r){
9366                 text = r.data[this.displayField];
9367             }else if(this.valueNotFoundText !== undefined){
9368                 text = this.valueNotFoundText;
9369             }
9370         }
9371         this.lastSelectionText = text;
9372         if(this.hiddenField){
9373             this.hiddenField.dom.value = v;
9374         }
9375         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9376         this.value = v;
9377     },
9378     /**
9379      * @property {Object} the last set data for the element
9380      */
9381     
9382     lastData : false,
9383     /**
9384      * Sets the value of the field based on a object which is related to the record format for the store.
9385      * @param {Object} value the value to set as. or false on reset?
9386      */
9387     setFromData : function(o){
9388         
9389         if(this.multiple){
9390             this.addItem(o);
9391             return;
9392         }
9393             
9394         var dv = ''; // display value
9395         var vv = ''; // value value..
9396         this.lastData = o;
9397         if (this.displayField) {
9398             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9399         } else {
9400             // this is an error condition!!!
9401             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9402         }
9403         
9404         if(this.valueField){
9405             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9406         }
9407         
9408         if(this.hiddenField){
9409             this.hiddenField.dom.value = vv;
9410             
9411             this.lastSelectionText = dv;
9412             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9413             this.value = vv;
9414             return;
9415         }
9416         // no hidden field.. - we store the value in 'value', but still display
9417         // display field!!!!
9418         this.lastSelectionText = dv;
9419         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9420         this.value = vv;
9421         
9422         
9423     },
9424     // private
9425     reset : function(){
9426         // overridden so that last data is reset..
9427         this.setValue(this.originalValue);
9428         this.clearInvalid();
9429         this.lastData = false;
9430         if (this.view) {
9431             this.view.clearSelections();
9432         }
9433     },
9434     // private
9435     findRecord : function(prop, value){
9436         var record;
9437         if(this.store.getCount() > 0){
9438             this.store.each(function(r){
9439                 if(r.data[prop] == value){
9440                     record = r;
9441                     return false;
9442                 }
9443                 return true;
9444             });
9445         }
9446         return record;
9447     },
9448     
9449     getName: function()
9450     {
9451         // returns hidden if it's set..
9452         if (!this.rendered) {return ''};
9453         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9454         
9455     },
9456     // private
9457     onViewMove : function(e, t){
9458         this.inKeyMode = false;
9459     },
9460
9461     // private
9462     onViewOver : function(e, t){
9463         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9464             return;
9465         }
9466         var item = this.view.findItemFromChild(t);
9467         if(item){
9468             var index = this.view.indexOf(item);
9469             this.select(index, false);
9470         }
9471     },
9472
9473     // private
9474     onViewClick : function(doFocus)
9475     {
9476         var index = this.view.getSelectedIndexes()[0];
9477         var r = this.store.getAt(index);
9478         if(r){
9479             this.onSelect(r, index);
9480         }
9481         if(doFocus !== false && !this.blockFocus){
9482             this.inputEl().focus();
9483         }
9484     },
9485
9486     // private
9487     restrictHeight : function(){
9488         //this.innerList.dom.style.height = '';
9489         //var inner = this.innerList.dom;
9490         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9491         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9492         //this.list.beginUpdate();
9493         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9494         this.list.alignTo(this.inputEl(), this.listAlign);
9495         //this.list.endUpdate();
9496     },
9497
9498     // private
9499     onEmptyResults : function(){
9500         this.collapse();
9501     },
9502
9503     /**
9504      * Returns true if the dropdown list is expanded, else false.
9505      */
9506     isExpanded : function(){
9507         return this.list.isVisible();
9508     },
9509
9510     /**
9511      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9512      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9513      * @param {String} value The data value of the item to select
9514      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9515      * selected item if it is not currently in view (defaults to true)
9516      * @return {Boolean} True if the value matched an item in the list, else false
9517      */
9518     selectByValue : function(v, scrollIntoView){
9519         if(v !== undefined && v !== null){
9520             var r = this.findRecord(this.valueField || this.displayField, v);
9521             if(r){
9522                 this.select(this.store.indexOf(r), scrollIntoView);
9523                 return true;
9524             }
9525         }
9526         return false;
9527     },
9528
9529     /**
9530      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9531      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9532      * @param {Number} index The zero-based index of the list item to select
9533      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9534      * selected item if it is not currently in view (defaults to true)
9535      */
9536     select : function(index, scrollIntoView){
9537         this.selectedIndex = index;
9538         this.view.select(index);
9539         if(scrollIntoView !== false){
9540             var el = this.view.getNode(index);
9541             if(el){
9542                 //this.innerList.scrollChildIntoView(el, false);
9543                 
9544             }
9545         }
9546     },
9547
9548     // private
9549     selectNext : function(){
9550         var ct = this.store.getCount();
9551         if(ct > 0){
9552             if(this.selectedIndex == -1){
9553                 this.select(0);
9554             }else if(this.selectedIndex < ct-1){
9555                 this.select(this.selectedIndex+1);
9556             }
9557         }
9558     },
9559
9560     // private
9561     selectPrev : function(){
9562         var ct = this.store.getCount();
9563         if(ct > 0){
9564             if(this.selectedIndex == -1){
9565                 this.select(0);
9566             }else if(this.selectedIndex != 0){
9567                 this.select(this.selectedIndex-1);
9568             }
9569         }
9570     },
9571
9572     // private
9573     onKeyUp : function(e){
9574         if(this.editable !== false && !e.isSpecialKey()){
9575             this.lastKey = e.getKey();
9576             this.dqTask.delay(this.queryDelay);
9577         }
9578     },
9579
9580     // private
9581     validateBlur : function(){
9582         return !this.list || !this.list.isVisible();   
9583     },
9584
9585     // private
9586     initQuery : function(){
9587         this.doQuery(this.getRawValue());
9588     },
9589
9590     // private
9591     doForce : function(){
9592         if(this.el.dom.value.length > 0){
9593             this.el.dom.value =
9594                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9595              
9596         }
9597     },
9598
9599     /**
9600      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9601      * query allowing the query action to be canceled if needed.
9602      * @param {String} query The SQL query to execute
9603      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9604      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9605      * saved in the current store (defaults to false)
9606      */
9607     doQuery : function(q, forceAll){
9608         
9609         if(q === undefined || q === null){
9610             q = '';
9611         }
9612         var qe = {
9613             query: q,
9614             forceAll: forceAll,
9615             combo: this,
9616             cancel:false
9617         };
9618         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9619             return false;
9620         }
9621         q = qe.query;
9622         
9623         forceAll = qe.forceAll;
9624         if(forceAll === true || (q.length >= this.minChars)){
9625             
9626             this.hasQuery = true;
9627             
9628             if(this.lastQuery != q || this.alwaysQuery){
9629                 this.lastQuery = q;
9630                 if(this.mode == 'local'){
9631                     this.selectedIndex = -1;
9632                     if(forceAll){
9633                         this.store.clearFilter();
9634                     }else{
9635                         this.store.filter(this.displayField, q);
9636                     }
9637                     this.onLoad();
9638                 }else{
9639                     this.store.baseParams[this.queryParam] = q;
9640                     
9641                     var options = {params : this.getParams(q)};
9642                     
9643                     if(this.loadNext){
9644                         options.add = true;
9645                         options.params.start = this.page * this.pageSize;
9646                     }
9647                     
9648                     this.store.load(options);
9649                     this.expand();
9650                 }
9651             }else{
9652                 this.selectedIndex = -1;
9653                 this.onLoad();   
9654             }
9655         }
9656         
9657         this.loadNext = false;
9658     },
9659
9660     // private
9661     getParams : function(q){
9662         var p = {};
9663         //p[this.queryParam] = q;
9664         
9665         if(this.pageSize){
9666             p.start = 0;
9667             p.limit = this.pageSize;
9668         }
9669         return p;
9670     },
9671
9672     /**
9673      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9674      */
9675     collapse : function(){
9676         if(!this.isExpanded()){
9677             return;
9678         }
9679         
9680         this.list.hide();
9681         Roo.get(document).un('mousedown', this.collapseIf, this);
9682         Roo.get(document).un('mousewheel', this.collapseIf, this);
9683         if (!this.editable) {
9684             Roo.get(document).un('keydown', this.listKeyPress, this);
9685         }
9686         this.fireEvent('collapse', this);
9687     },
9688
9689     // private
9690     collapseIf : function(e){
9691         var in_combo  = e.within(this.el);
9692         var in_list =  e.within(this.list);
9693         
9694         if (in_combo || in_list) {
9695             //e.stopPropagation();
9696             return;
9697         }
9698
9699         this.collapse();
9700         
9701     },
9702
9703     /**
9704      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9705      */
9706     expand : function(){
9707        
9708         if(this.isExpanded() || !this.hasFocus){
9709             return;
9710         }
9711          Roo.log('expand');
9712         this.list.alignTo(this.inputEl(), this.listAlign);
9713         this.list.show();
9714         Roo.get(document).on('mousedown', this.collapseIf, this);
9715         Roo.get(document).on('mousewheel', this.collapseIf, this);
9716         if (!this.editable) {
9717             Roo.get(document).on('keydown', this.listKeyPress, this);
9718         }
9719         
9720         this.fireEvent('expand', this);
9721     },
9722
9723     // private
9724     // Implements the default empty TriggerField.onTriggerClick function
9725     onTriggerClick : function()
9726     {
9727         Roo.log('trigger click');
9728         
9729         if(this.disabled){
9730             return;
9731         }
9732         
9733         this.page = 0;
9734         this.loadNext = false;
9735         
9736         if(this.isExpanded()){
9737             this.collapse();
9738             if (!this.blockFocus) {
9739                 this.inputEl().focus();
9740             }
9741             
9742         }else {
9743             this.hasFocus = true;
9744             if(this.triggerAction == 'all') {
9745                 this.doQuery(this.allQuery, true);
9746             } else {
9747                 this.doQuery(this.getRawValue());
9748             }
9749             if (!this.blockFocus) {
9750                 this.inputEl().focus();
9751             }
9752         }
9753     },
9754     listKeyPress : function(e)
9755     {
9756         //Roo.log('listkeypress');
9757         // scroll to first matching element based on key pres..
9758         if (e.isSpecialKey()) {
9759             return false;
9760         }
9761         var k = String.fromCharCode(e.getKey()).toUpperCase();
9762         //Roo.log(k);
9763         var match  = false;
9764         var csel = this.view.getSelectedNodes();
9765         var cselitem = false;
9766         if (csel.length) {
9767             var ix = this.view.indexOf(csel[0]);
9768             cselitem  = this.store.getAt(ix);
9769             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
9770                 cselitem = false;
9771             }
9772             
9773         }
9774         
9775         this.store.each(function(v) { 
9776             if (cselitem) {
9777                 // start at existing selection.
9778                 if (cselitem.id == v.id) {
9779                     cselitem = false;
9780                 }
9781                 return true;
9782             }
9783                 
9784             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9785                 match = this.store.indexOf(v);
9786                 return false;
9787             }
9788             return true;
9789         }, this);
9790         
9791         if (match === false) {
9792             return true; // no more action?
9793         }
9794         // scroll to?
9795         this.view.select(match);
9796         var sn = Roo.get(this.view.getSelectedNodes()[0])
9797         //sn.scrollIntoView(sn.dom.parentNode, false);
9798     },
9799     
9800     onViewScroll : function(e, t){
9801         
9802         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9803             return;
9804         }
9805         
9806         this.hasQuery = true;
9807         
9808         this.loading = this.list.select('.loading', true).first();
9809         
9810         if(this.loading === null){
9811             this.list.createChild({
9812                 tag: 'div',
9813                 cls: 'loading select2-more-results select2-active',
9814                 html: 'Loading more results...'
9815             })
9816             
9817             this.loading = this.list.select('.loading', true).first();
9818             
9819             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9820             
9821             this.loading.hide();
9822         }
9823         
9824         this.loading.show();
9825         
9826         var _combo = this;
9827         
9828         this.page++;
9829         this.loadNext = true;
9830         
9831         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9832         
9833         return;
9834     },
9835     
9836     addItem : function(o)
9837     {   
9838         var dv = ''; // display value
9839         
9840         if (this.displayField) {
9841             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9842         } else {
9843             // this is an error condition!!!
9844             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9845         }
9846         
9847         if(!dv.length){
9848             return;
9849         }
9850         
9851         var choice = this.choices.createChild({
9852             tag: 'li',
9853             cls: 'select2-search-choice',
9854             cn: [
9855                 {
9856                     tag: 'div',
9857                     html: dv
9858                 },
9859                 {
9860                     tag: 'a',
9861                     href: '#',
9862                     cls: 'select2-search-choice-close',
9863                     tabindex: '-1'
9864                 }
9865             ]
9866             
9867         }, this.searchField);
9868         
9869         var close = choice.select('a.select2-search-choice-close', true).first()
9870         
9871         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9872         
9873         this.item.push(o);
9874         this.lastData = o;
9875         
9876         this.syncValue();
9877         
9878         this.inputEl().dom.value = '';
9879         
9880     },
9881     
9882     onRemoveItem : function(e, _self, o)
9883     {
9884         Roo.log('remove item');
9885         var index = this.item.indexOf(o.data) * 1;
9886         
9887         if( index < 0){
9888             Roo.log('not this item?!');
9889             return;
9890         }
9891         
9892         this.item.splice(index, 1);
9893         o.item.remove();
9894         
9895         this.syncValue();
9896         
9897         this.fireEvent('remove', this);
9898         
9899     },
9900     
9901     syncValue : function()
9902     {
9903         if(!this.item.length){
9904             this.clearValue();
9905             return;
9906         }
9907             
9908         var value = [];
9909         var _this = this;
9910         Roo.each(this.item, function(i){
9911             if(_this.valueField){
9912                 value.push(i[_this.valueField]);
9913                 return;
9914             }
9915
9916             value.push(i);
9917         });
9918
9919         this.value = value.join(',');
9920
9921         if(this.hiddenField){
9922             this.hiddenField.dom.value = this.value;
9923         }
9924     },
9925     
9926     clearItem : function()
9927     {
9928         if(!this.multiple){
9929             return;
9930         }
9931         
9932         this.item = [];
9933         
9934         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9935            c.remove();
9936         });
9937         
9938         this.syncValue();
9939     }
9940     
9941     
9942
9943     /** 
9944     * @cfg {Boolean} grow 
9945     * @hide 
9946     */
9947     /** 
9948     * @cfg {Number} growMin 
9949     * @hide 
9950     */
9951     /** 
9952     * @cfg {Number} growMax 
9953     * @hide 
9954     */
9955     /**
9956      * @hide
9957      * @method autoSize
9958      */
9959 });
9960 /*
9961  * Based on:
9962  * Ext JS Library 1.1.1
9963  * Copyright(c) 2006-2007, Ext JS, LLC.
9964  *
9965  * Originally Released Under LGPL - original licence link has changed is not relivant.
9966  *
9967  * Fork - LGPL
9968  * <script type="text/javascript">
9969  */
9970
9971 /**
9972  * @class Roo.View
9973  * @extends Roo.util.Observable
9974  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9975  * This class also supports single and multi selection modes. <br>
9976  * Create a data model bound view:
9977  <pre><code>
9978  var store = new Roo.data.Store(...);
9979
9980  var view = new Roo.View({
9981     el : "my-element",
9982     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9983  
9984     singleSelect: true,
9985     selectedClass: "ydataview-selected",
9986     store: store
9987  });
9988
9989  // listen for node click?
9990  view.on("click", function(vw, index, node, e){
9991  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9992  });
9993
9994  // load XML data
9995  dataModel.load("foobar.xml");
9996  </code></pre>
9997  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9998  * <br><br>
9999  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10000  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10001  * 
10002  * Note: old style constructor is still suported (container, template, config)
10003  * 
10004  * @constructor
10005  * Create a new View
10006  * @param {Object} config The config object
10007  * 
10008  */
10009 Roo.View = function(config, depreciated_tpl, depreciated_config){
10010     
10011     if (typeof(depreciated_tpl) == 'undefined') {
10012         // new way.. - universal constructor.
10013         Roo.apply(this, config);
10014         this.el  = Roo.get(this.el);
10015     } else {
10016         // old format..
10017         this.el  = Roo.get(config);
10018         this.tpl = depreciated_tpl;
10019         Roo.apply(this, depreciated_config);
10020     }
10021     this.wrapEl  = this.el.wrap().wrap();
10022     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10023     
10024     
10025     if(typeof(this.tpl) == "string"){
10026         this.tpl = new Roo.Template(this.tpl);
10027     } else {
10028         // support xtype ctors..
10029         this.tpl = new Roo.factory(this.tpl, Roo);
10030     }
10031     
10032     
10033     this.tpl.compile();
10034    
10035   
10036     
10037      
10038     /** @private */
10039     this.addEvents({
10040         /**
10041          * @event beforeclick
10042          * Fires before a click is processed. Returns false to cancel the default action.
10043          * @param {Roo.View} this
10044          * @param {Number} index The index of the target node
10045          * @param {HTMLElement} node The target node
10046          * @param {Roo.EventObject} e The raw event object
10047          */
10048             "beforeclick" : true,
10049         /**
10050          * @event click
10051          * Fires when a template node is clicked.
10052          * @param {Roo.View} this
10053          * @param {Number} index The index of the target node
10054          * @param {HTMLElement} node The target node
10055          * @param {Roo.EventObject} e The raw event object
10056          */
10057             "click" : true,
10058         /**
10059          * @event dblclick
10060          * Fires when a template node is double clicked.
10061          * @param {Roo.View} this
10062          * @param {Number} index The index of the target node
10063          * @param {HTMLElement} node The target node
10064          * @param {Roo.EventObject} e The raw event object
10065          */
10066             "dblclick" : true,
10067         /**
10068          * @event contextmenu
10069          * Fires when a template node is right clicked.
10070          * @param {Roo.View} this
10071          * @param {Number} index The index of the target node
10072          * @param {HTMLElement} node The target node
10073          * @param {Roo.EventObject} e The raw event object
10074          */
10075             "contextmenu" : true,
10076         /**
10077          * @event selectionchange
10078          * Fires when the selected nodes change.
10079          * @param {Roo.View} this
10080          * @param {Array} selections Array of the selected nodes
10081          */
10082             "selectionchange" : true,
10083     
10084         /**
10085          * @event beforeselect
10086          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10087          * @param {Roo.View} this
10088          * @param {HTMLElement} node The node to be selected
10089          * @param {Array} selections Array of currently selected nodes
10090          */
10091             "beforeselect" : true,
10092         /**
10093          * @event preparedata
10094          * Fires on every row to render, to allow you to change the data.
10095          * @param {Roo.View} this
10096          * @param {Object} data to be rendered (change this)
10097          */
10098           "preparedata" : true
10099           
10100           
10101         });
10102
10103
10104
10105     this.el.on({
10106         "click": this.onClick,
10107         "dblclick": this.onDblClick,
10108         "contextmenu": this.onContextMenu,
10109         scope:this
10110     });
10111
10112     this.selections = [];
10113     this.nodes = [];
10114     this.cmp = new Roo.CompositeElementLite([]);
10115     if(this.store){
10116         this.store = Roo.factory(this.store, Roo.data);
10117         this.setStore(this.store, true);
10118     }
10119     
10120     if ( this.footer && this.footer.xtype) {
10121            
10122          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10123         
10124         this.footer.dataSource = this.store
10125         this.footer.container = fctr;
10126         this.footer = Roo.factory(this.footer, Roo);
10127         fctr.insertFirst(this.el);
10128         
10129         // this is a bit insane - as the paging toolbar seems to detach the el..
10130 //        dom.parentNode.parentNode.parentNode
10131          // they get detached?
10132     }
10133     
10134     
10135     Roo.View.superclass.constructor.call(this);
10136     
10137     
10138 };
10139
10140 Roo.extend(Roo.View, Roo.util.Observable, {
10141     
10142      /**
10143      * @cfg {Roo.data.Store} store Data store to load data from.
10144      */
10145     store : false,
10146     
10147     /**
10148      * @cfg {String|Roo.Element} el The container element.
10149      */
10150     el : '',
10151     
10152     /**
10153      * @cfg {String|Roo.Template} tpl The template used by this View 
10154      */
10155     tpl : false,
10156     /**
10157      * @cfg {String} dataName the named area of the template to use as the data area
10158      *                          Works with domtemplates roo-name="name"
10159      */
10160     dataName: false,
10161     /**
10162      * @cfg {String} selectedClass The css class to add to selected nodes
10163      */
10164     selectedClass : "x-view-selected",
10165      /**
10166      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10167      */
10168     emptyText : "",
10169     
10170     /**
10171      * @cfg {String} text to display on mask (default Loading)
10172      */
10173     mask : false,
10174     /**
10175      * @cfg {Boolean} multiSelect Allow multiple selection
10176      */
10177     multiSelect : false,
10178     /**
10179      * @cfg {Boolean} singleSelect Allow single selection
10180      */
10181     singleSelect:  false,
10182     
10183     /**
10184      * @cfg {Boolean} toggleSelect - selecting 
10185      */
10186     toggleSelect : false,
10187     
10188     /**
10189      * Returns the element this view is bound to.
10190      * @return {Roo.Element}
10191      */
10192     getEl : function(){
10193         return this.wrapEl;
10194     },
10195     
10196     
10197
10198     /**
10199      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10200      */
10201     refresh : function(){
10202         Roo.log('refresh');
10203         var t = this.tpl;
10204         
10205         // if we are using something like 'domtemplate', then
10206         // the what gets used is:
10207         // t.applySubtemplate(NAME, data, wrapping data..)
10208         // the outer template then get' applied with
10209         //     the store 'extra data'
10210         // and the body get's added to the
10211         //      roo-name="data" node?
10212         //      <span class='roo-tpl-{name}'></span> ?????
10213         
10214         
10215         
10216         this.clearSelections();
10217         this.el.update("");
10218         var html = [];
10219         var records = this.store.getRange();
10220         if(records.length < 1) {
10221             
10222             // is this valid??  = should it render a template??
10223             
10224             this.el.update(this.emptyText);
10225             return;
10226         }
10227         var el = this.el;
10228         if (this.dataName) {
10229             this.el.update(t.apply(this.store.meta)); //????
10230             el = this.el.child('.roo-tpl-' + this.dataName);
10231         }
10232         
10233         for(var i = 0, len = records.length; i < len; i++){
10234             var data = this.prepareData(records[i].data, i, records[i]);
10235             this.fireEvent("preparedata", this, data, i, records[i]);
10236             html[html.length] = Roo.util.Format.trim(
10237                 this.dataName ?
10238                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10239                     t.apply(data)
10240             );
10241         }
10242         
10243         
10244         
10245         el.update(html.join(""));
10246         this.nodes = el.dom.childNodes;
10247         this.updateIndexes(0);
10248     },
10249     
10250
10251     /**
10252      * Function to override to reformat the data that is sent to
10253      * the template for each node.
10254      * DEPRICATED - use the preparedata event handler.
10255      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10256      * a JSON object for an UpdateManager bound view).
10257      */
10258     prepareData : function(data, index, record)
10259     {
10260         this.fireEvent("preparedata", this, data, index, record);
10261         return data;
10262     },
10263
10264     onUpdate : function(ds, record){
10265          Roo.log('on update');   
10266         this.clearSelections();
10267         var index = this.store.indexOf(record);
10268         var n = this.nodes[index];
10269         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10270         n.parentNode.removeChild(n);
10271         this.updateIndexes(index, index);
10272     },
10273
10274     
10275     
10276 // --------- FIXME     
10277     onAdd : function(ds, records, index)
10278     {
10279         Roo.log(['on Add', ds, records, index] );        
10280         this.clearSelections();
10281         if(this.nodes.length == 0){
10282             this.refresh();
10283             return;
10284         }
10285         var n = this.nodes[index];
10286         for(var i = 0, len = records.length; i < len; i++){
10287             var d = this.prepareData(records[i].data, i, records[i]);
10288             if(n){
10289                 this.tpl.insertBefore(n, d);
10290             }else{
10291                 
10292                 this.tpl.append(this.el, d);
10293             }
10294         }
10295         this.updateIndexes(index);
10296     },
10297
10298     onRemove : function(ds, record, index){
10299         Roo.log('onRemove');
10300         this.clearSelections();
10301         var el = this.dataName  ?
10302             this.el.child('.roo-tpl-' + this.dataName) :
10303             this.el; 
10304         
10305         el.dom.removeChild(this.nodes[index]);
10306         this.updateIndexes(index);
10307     },
10308
10309     /**
10310      * Refresh an individual node.
10311      * @param {Number} index
10312      */
10313     refreshNode : function(index){
10314         this.onUpdate(this.store, this.store.getAt(index));
10315     },
10316
10317     updateIndexes : function(startIndex, endIndex){
10318         var ns = this.nodes;
10319         startIndex = startIndex || 0;
10320         endIndex = endIndex || ns.length - 1;
10321         for(var i = startIndex; i <= endIndex; i++){
10322             ns[i].nodeIndex = i;
10323         }
10324     },
10325
10326     /**
10327      * Changes the data store this view uses and refresh the view.
10328      * @param {Store} store
10329      */
10330     setStore : function(store, initial){
10331         if(!initial && this.store){
10332             this.store.un("datachanged", this.refresh);
10333             this.store.un("add", this.onAdd);
10334             this.store.un("remove", this.onRemove);
10335             this.store.un("update", this.onUpdate);
10336             this.store.un("clear", this.refresh);
10337             this.store.un("beforeload", this.onBeforeLoad);
10338             this.store.un("load", this.onLoad);
10339             this.store.un("loadexception", this.onLoad);
10340         }
10341         if(store){
10342           
10343             store.on("datachanged", this.refresh, this);
10344             store.on("add", this.onAdd, this);
10345             store.on("remove", this.onRemove, this);
10346             store.on("update", this.onUpdate, this);
10347             store.on("clear", this.refresh, this);
10348             store.on("beforeload", this.onBeforeLoad, this);
10349             store.on("load", this.onLoad, this);
10350             store.on("loadexception", this.onLoad, this);
10351         }
10352         
10353         if(store){
10354             this.refresh();
10355         }
10356     },
10357     /**
10358      * onbeforeLoad - masks the loading area.
10359      *
10360      */
10361     onBeforeLoad : function(store,opts)
10362     {
10363          Roo.log('onBeforeLoad');   
10364         if (!opts.add) {
10365             this.el.update("");
10366         }
10367         this.el.mask(this.mask ? this.mask : "Loading" ); 
10368     },
10369     onLoad : function ()
10370     {
10371         this.el.unmask();
10372     },
10373     
10374
10375     /**
10376      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10377      * @param {HTMLElement} node
10378      * @return {HTMLElement} The template node
10379      */
10380     findItemFromChild : function(node){
10381         var el = this.dataName  ?
10382             this.el.child('.roo-tpl-' + this.dataName,true) :
10383             this.el.dom; 
10384         
10385         if(!node || node.parentNode == el){
10386                     return node;
10387             }
10388             var p = node.parentNode;
10389             while(p && p != el){
10390             if(p.parentNode == el){
10391                 return p;
10392             }
10393             p = p.parentNode;
10394         }
10395             return null;
10396     },
10397
10398     /** @ignore */
10399     onClick : function(e){
10400         var item = this.findItemFromChild(e.getTarget());
10401         if(item){
10402             var index = this.indexOf(item);
10403             if(this.onItemClick(item, index, e) !== false){
10404                 this.fireEvent("click", this, index, item, e);
10405             }
10406         }else{
10407             this.clearSelections();
10408         }
10409     },
10410
10411     /** @ignore */
10412     onContextMenu : function(e){
10413         var item = this.findItemFromChild(e.getTarget());
10414         if(item){
10415             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10416         }
10417     },
10418
10419     /** @ignore */
10420     onDblClick : function(e){
10421         var item = this.findItemFromChild(e.getTarget());
10422         if(item){
10423             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10424         }
10425     },
10426
10427     onItemClick : function(item, index, e)
10428     {
10429         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10430             return false;
10431         }
10432         if (this.toggleSelect) {
10433             var m = this.isSelected(item) ? 'unselect' : 'select';
10434             Roo.log(m);
10435             var _t = this;
10436             _t[m](item, true, false);
10437             return true;
10438         }
10439         if(this.multiSelect || this.singleSelect){
10440             if(this.multiSelect && e.shiftKey && this.lastSelection){
10441                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10442             }else{
10443                 this.select(item, this.multiSelect && e.ctrlKey);
10444                 this.lastSelection = item;
10445             }
10446             e.preventDefault();
10447         }
10448         return true;
10449     },
10450
10451     /**
10452      * Get the number of selected nodes.
10453      * @return {Number}
10454      */
10455     getSelectionCount : function(){
10456         return this.selections.length;
10457     },
10458
10459     /**
10460      * Get the currently selected nodes.
10461      * @return {Array} An array of HTMLElements
10462      */
10463     getSelectedNodes : function(){
10464         return this.selections;
10465     },
10466
10467     /**
10468      * Get the indexes of the selected nodes.
10469      * @return {Array}
10470      */
10471     getSelectedIndexes : function(){
10472         var indexes = [], s = this.selections;
10473         for(var i = 0, len = s.length; i < len; i++){
10474             indexes.push(s[i].nodeIndex);
10475         }
10476         return indexes;
10477     },
10478
10479     /**
10480      * Clear all selections
10481      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10482      */
10483     clearSelections : function(suppressEvent){
10484         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10485             this.cmp.elements = this.selections;
10486             this.cmp.removeClass(this.selectedClass);
10487             this.selections = [];
10488             if(!suppressEvent){
10489                 this.fireEvent("selectionchange", this, this.selections);
10490             }
10491         }
10492     },
10493
10494     /**
10495      * Returns true if the passed node is selected
10496      * @param {HTMLElement/Number} node The node or node index
10497      * @return {Boolean}
10498      */
10499     isSelected : function(node){
10500         var s = this.selections;
10501         if(s.length < 1){
10502             return false;
10503         }
10504         node = this.getNode(node);
10505         return s.indexOf(node) !== -1;
10506     },
10507
10508     /**
10509      * Selects nodes.
10510      * @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
10511      * @param {Boolean} keepExisting (optional) true to keep existing selections
10512      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10513      */
10514     select : function(nodeInfo, keepExisting, suppressEvent){
10515         if(nodeInfo instanceof Array){
10516             if(!keepExisting){
10517                 this.clearSelections(true);
10518             }
10519             for(var i = 0, len = nodeInfo.length; i < len; i++){
10520                 this.select(nodeInfo[i], true, true);
10521             }
10522             return;
10523         } 
10524         var node = this.getNode(nodeInfo);
10525         if(!node || this.isSelected(node)){
10526             return; // already selected.
10527         }
10528         if(!keepExisting){
10529             this.clearSelections(true);
10530         }
10531         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10532             Roo.fly(node).addClass(this.selectedClass);
10533             this.selections.push(node);
10534             if(!suppressEvent){
10535                 this.fireEvent("selectionchange", this, this.selections);
10536             }
10537         }
10538         
10539         
10540     },
10541       /**
10542      * Unselects nodes.
10543      * @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
10544      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10545      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10546      */
10547     unselect : function(nodeInfo, keepExisting, suppressEvent)
10548     {
10549         if(nodeInfo instanceof Array){
10550             Roo.each(this.selections, function(s) {
10551                 this.unselect(s, nodeInfo);
10552             }, this);
10553             return;
10554         }
10555         var node = this.getNode(nodeInfo);
10556         if(!node || !this.isSelected(node)){
10557             Roo.log("not selected");
10558             return; // not selected.
10559         }
10560         // fireevent???
10561         var ns = [];
10562         Roo.each(this.selections, function(s) {
10563             if (s == node ) {
10564                 Roo.fly(node).removeClass(this.selectedClass);
10565
10566                 return;
10567             }
10568             ns.push(s);
10569         },this);
10570         
10571         this.selections= ns;
10572         this.fireEvent("selectionchange", this, this.selections);
10573     },
10574
10575     /**
10576      * Gets a template node.
10577      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10578      * @return {HTMLElement} The node or null if it wasn't found
10579      */
10580     getNode : function(nodeInfo){
10581         if(typeof nodeInfo == "string"){
10582             return document.getElementById(nodeInfo);
10583         }else if(typeof nodeInfo == "number"){
10584             return this.nodes[nodeInfo];
10585         }
10586         return nodeInfo;
10587     },
10588
10589     /**
10590      * Gets a range template nodes.
10591      * @param {Number} startIndex
10592      * @param {Number} endIndex
10593      * @return {Array} An array of nodes
10594      */
10595     getNodes : function(start, end){
10596         var ns = this.nodes;
10597         start = start || 0;
10598         end = typeof end == "undefined" ? ns.length - 1 : end;
10599         var nodes = [];
10600         if(start <= end){
10601             for(var i = start; i <= end; i++){
10602                 nodes.push(ns[i]);
10603             }
10604         } else{
10605             for(var i = start; i >= end; i--){
10606                 nodes.push(ns[i]);
10607             }
10608         }
10609         return nodes;
10610     },
10611
10612     /**
10613      * Finds the index of the passed node
10614      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10615      * @return {Number} The index of the node or -1
10616      */
10617     indexOf : function(node){
10618         node = this.getNode(node);
10619         if(typeof node.nodeIndex == "number"){
10620             return node.nodeIndex;
10621         }
10622         var ns = this.nodes;
10623         for(var i = 0, len = ns.length; i < len; i++){
10624             if(ns[i] == node){
10625                 return i;
10626             }
10627         }
10628         return -1;
10629     }
10630 });
10631 /*
10632  * - LGPL
10633  *
10634  * based on jquery fullcalendar
10635  * 
10636  */
10637
10638 Roo.bootstrap = Roo.bootstrap || {};
10639 /**
10640  * @class Roo.bootstrap.Calendar
10641  * @extends Roo.bootstrap.Component
10642  * Bootstrap Calendar class
10643  * @cfg {Boolean} loadMask (true|false) default false
10644  * @cfg {Object} header generate the user specific header of the calendar, default false
10645
10646  * @constructor
10647  * Create a new Container
10648  * @param {Object} config The config object
10649  */
10650
10651
10652
10653 Roo.bootstrap.Calendar = function(config){
10654     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10655      this.addEvents({
10656         /**
10657              * @event select
10658              * Fires when a date is selected
10659              * @param {DatePicker} this
10660              * @param {Date} date The selected date
10661              */
10662         'select': true,
10663         /**
10664              * @event monthchange
10665              * Fires when the displayed month changes 
10666              * @param {DatePicker} this
10667              * @param {Date} date The selected month
10668              */
10669         'monthchange': true,
10670         /**
10671              * @event evententer
10672              * Fires when mouse over an event
10673              * @param {Calendar} this
10674              * @param {event} Event
10675              */
10676         'evententer': true,
10677         /**
10678              * @event eventleave
10679              * Fires when the mouse leaves an
10680              * @param {Calendar} this
10681              * @param {event}
10682              */
10683         'eventleave': true,
10684         /**
10685              * @event eventclick
10686              * Fires when the mouse click an
10687              * @param {Calendar} this
10688              * @param {event}
10689              */
10690         'eventclick': true
10691         
10692     });
10693
10694 };
10695
10696 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10697     
10698      /**
10699      * @cfg {Number} startDay
10700      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10701      */
10702     startDay : 0,
10703     
10704     loadMask : false,
10705     
10706     header : false,
10707       
10708     getAutoCreate : function(){
10709         
10710         
10711         var fc_button = function(name, corner, style, content ) {
10712             return Roo.apply({},{
10713                 tag : 'span',
10714                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10715                          (corner.length ?
10716                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10717                             ''
10718                         ),
10719                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10720                 unselectable: 'on'
10721             });
10722         };
10723         
10724         var header = {};
10725         
10726         if(!this.header){
10727             header = {
10728                 tag : 'table',
10729                 cls : 'fc-header',
10730                 style : 'width:100%',
10731                 cn : [
10732                     {
10733                         tag: 'tr',
10734                         cn : [
10735                             {
10736                                 tag : 'td',
10737                                 cls : 'fc-header-left',
10738                                 cn : [
10739                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
10740                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
10741                                     { tag: 'span', cls: 'fc-header-space' },
10742                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
10743
10744
10745                                 ]
10746                             },
10747
10748                             {
10749                                 tag : 'td',
10750                                 cls : 'fc-header-center',
10751                                 cn : [
10752                                     {
10753                                         tag: 'span',
10754                                         cls: 'fc-header-title',
10755                                         cn : {
10756                                             tag: 'H2',
10757                                             html : 'month / year'
10758                                         }
10759                                     }
10760
10761                                 ]
10762                             },
10763                             {
10764                                 tag : 'td',
10765                                 cls : 'fc-header-right',
10766                                 cn : [
10767                               /*      fc_button('month', 'left', '', 'month' ),
10768                                     fc_button('week', '', '', 'week' ),
10769                                     fc_button('day', 'right', '', 'day' )
10770                                 */    
10771
10772                                 ]
10773                             }
10774
10775                         ]
10776                     }
10777                 ]
10778             };
10779         }
10780         
10781         header = this.header;
10782         
10783        
10784         var cal_heads = function() {
10785             var ret = [];
10786             // fixme - handle this.
10787             
10788             for (var i =0; i < Date.dayNames.length; i++) {
10789                 var d = Date.dayNames[i];
10790                 ret.push({
10791                     tag: 'th',
10792                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10793                     html : d.substring(0,3)
10794                 });
10795                 
10796             }
10797             ret[0].cls += ' fc-first';
10798             ret[6].cls += ' fc-last';
10799             return ret;
10800         };
10801         var cal_cell = function(n) {
10802             return  {
10803                 tag: 'td',
10804                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10805                 cn : [
10806                     {
10807                         cn : [
10808                             {
10809                                 cls: 'fc-day-number',
10810                                 html: 'D'
10811                             },
10812                             {
10813                                 cls: 'fc-day-content',
10814                              
10815                                 cn : [
10816                                      {
10817                                         style: 'position: relative;' // height: 17px;
10818                                     }
10819                                 ]
10820                             }
10821                             
10822                             
10823                         ]
10824                     }
10825                 ]
10826                 
10827             }
10828         };
10829         var cal_rows = function() {
10830             
10831             var ret = []
10832             for (var r = 0; r < 6; r++) {
10833                 var row= {
10834                     tag : 'tr',
10835                     cls : 'fc-week',
10836                     cn : []
10837                 };
10838                 
10839                 for (var i =0; i < Date.dayNames.length; i++) {
10840                     var d = Date.dayNames[i];
10841                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10842
10843                 }
10844                 row.cn[0].cls+=' fc-first';
10845                 row.cn[0].cn[0].style = 'min-height:90px';
10846                 row.cn[6].cls+=' fc-last';
10847                 ret.push(row);
10848                 
10849             }
10850             ret[0].cls += ' fc-first';
10851             ret[4].cls += ' fc-prev-last';
10852             ret[5].cls += ' fc-last';
10853             return ret;
10854             
10855         };
10856         
10857         var cal_table = {
10858             tag: 'table',
10859             cls: 'fc-border-separate',
10860             style : 'width:100%',
10861             cellspacing  : 0,
10862             cn : [
10863                 { 
10864                     tag: 'thead',
10865                     cn : [
10866                         { 
10867                             tag: 'tr',
10868                             cls : 'fc-first fc-last',
10869                             cn : cal_heads()
10870                         }
10871                     ]
10872                 },
10873                 { 
10874                     tag: 'tbody',
10875                     cn : cal_rows()
10876                 }
10877                   
10878             ]
10879         };
10880          
10881          var cfg = {
10882             cls : 'fc fc-ltr',
10883             cn : [
10884                 header,
10885                 {
10886                     cls : 'fc-content',
10887                     style : "position: relative;",
10888                     cn : [
10889                         {
10890                             cls : 'fc-view fc-view-month fc-grid',
10891                             style : 'position: relative',
10892                             unselectable : 'on',
10893                             cn : [
10894                                 {
10895                                     cls : 'fc-event-container',
10896                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10897                                 },
10898                                 cal_table
10899                             ]
10900                         }
10901                     ]
10902     
10903                 }
10904            ] 
10905             
10906         };
10907         
10908          
10909         
10910         return cfg;
10911     },
10912     
10913     
10914     initEvents : function()
10915     {
10916         if(!this.store){
10917             throw "can not find store for calendar";
10918         }
10919         
10920         var mark = {
10921             tag: "div",
10922             cls:"x-dlg-mask",
10923             style: "text-align:center",
10924             cn: [
10925                 {
10926                     tag: "div",
10927                     style: "background-color:white;width:50%;margin:250 auto",
10928                     cn: [
10929                         {
10930                             tag: "img",
10931                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10932                         },
10933                         {
10934                             tag: "span",
10935                             html: "Loading"
10936                         }
10937                         
10938                     ]
10939                 }
10940             ]
10941         }
10942         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10943         
10944         var size = this.el.select('.fc-content', true).first().getSize();
10945         this.maskEl.setSize(size.width, size.height);
10946         this.maskEl.enableDisplayMode("block");
10947         if(!this.loadMask){
10948             this.maskEl.hide();
10949         }
10950         
10951         this.store = Roo.factory(this.store, Roo.data);
10952         this.store.on('load', this.onLoad, this);
10953         this.store.on('beforeload', this.onBeforeLoad, this);
10954         
10955         this.resize();
10956         
10957         this.cells = this.el.select('.fc-day',true);
10958         //Roo.log(this.cells);
10959         this.textNodes = this.el.query('.fc-day-number');
10960         this.cells.addClassOnOver('fc-state-hover');
10961         
10962         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10963         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10964         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10965         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10966         
10967         this.on('monthchange', this.onMonthChange, this);
10968         
10969         this.update(new Date().clearTime());
10970     },
10971     
10972     resize : function() {
10973         var sz  = this.el.getSize();
10974         
10975         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10976         this.el.select('.fc-day-content div',true).setHeight(34);
10977     },
10978     
10979     
10980     // private
10981     showPrevMonth : function(e){
10982         this.update(this.activeDate.add("mo", -1));
10983     },
10984     showToday : function(e){
10985         this.update(new Date().clearTime());
10986     },
10987     // private
10988     showNextMonth : function(e){
10989         this.update(this.activeDate.add("mo", 1));
10990     },
10991
10992     // private
10993     showPrevYear : function(){
10994         this.update(this.activeDate.add("y", -1));
10995     },
10996
10997     // private
10998     showNextYear : function(){
10999         this.update(this.activeDate.add("y", 1));
11000     },
11001
11002     
11003    // private
11004     update : function(date)
11005     {
11006         var vd = this.activeDate;
11007         this.activeDate = date;
11008 //        if(vd && this.el){
11009 //            var t = date.getTime();
11010 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11011 //                Roo.log('using add remove');
11012 //                
11013 //                this.fireEvent('monthchange', this, date);
11014 //                
11015 //                this.cells.removeClass("fc-state-highlight");
11016 //                this.cells.each(function(c){
11017 //                   if(c.dateValue == t){
11018 //                       c.addClass("fc-state-highlight");
11019 //                       setTimeout(function(){
11020 //                            try{c.dom.firstChild.focus();}catch(e){}
11021 //                       }, 50);
11022 //                       return false;
11023 //                   }
11024 //                   return true;
11025 //                });
11026 //                return;
11027 //            }
11028 //        }
11029         
11030         var days = date.getDaysInMonth();
11031         
11032         var firstOfMonth = date.getFirstDateOfMonth();
11033         var startingPos = firstOfMonth.getDay()-this.startDay;
11034         
11035         if(startingPos < this.startDay){
11036             startingPos += 7;
11037         }
11038         
11039         var pm = date.add(Date.MONTH, -1);
11040         var prevStart = pm.getDaysInMonth()-startingPos;
11041 //        
11042         this.cells = this.el.select('.fc-day',true);
11043         this.textNodes = this.el.query('.fc-day-number');
11044         this.cells.addClassOnOver('fc-state-hover');
11045         
11046         var cells = this.cells.elements;
11047         var textEls = this.textNodes;
11048         
11049         Roo.each(cells, function(cell){
11050             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11051         });
11052         
11053         days += startingPos;
11054
11055         // convert everything to numbers so it's fast
11056         var day = 86400000;
11057         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11058         //Roo.log(d);
11059         //Roo.log(pm);
11060         //Roo.log(prevStart);
11061         
11062         var today = new Date().clearTime().getTime();
11063         var sel = date.clearTime().getTime();
11064         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11065         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11066         var ddMatch = this.disabledDatesRE;
11067         var ddText = this.disabledDatesText;
11068         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11069         var ddaysText = this.disabledDaysText;
11070         var format = this.format;
11071         
11072         var setCellClass = function(cal, cell){
11073             
11074             //Roo.log('set Cell Class');
11075             cell.title = "";
11076             var t = d.getTime();
11077             
11078             //Roo.log(d);
11079             
11080             cell.dateValue = t;
11081             if(t == today){
11082                 cell.className += " fc-today";
11083                 cell.className += " fc-state-highlight";
11084                 cell.title = cal.todayText;
11085             }
11086             if(t == sel){
11087                 // disable highlight in other month..
11088                 //cell.className += " fc-state-highlight";
11089                 
11090             }
11091             // disabling
11092             if(t < min) {
11093                 cell.className = " fc-state-disabled";
11094                 cell.title = cal.minText;
11095                 return;
11096             }
11097             if(t > max) {
11098                 cell.className = " fc-state-disabled";
11099                 cell.title = cal.maxText;
11100                 return;
11101             }
11102             if(ddays){
11103                 if(ddays.indexOf(d.getDay()) != -1){
11104                     cell.title = ddaysText;
11105                     cell.className = " fc-state-disabled";
11106                 }
11107             }
11108             if(ddMatch && format){
11109                 var fvalue = d.dateFormat(format);
11110                 if(ddMatch.test(fvalue)){
11111                     cell.title = ddText.replace("%0", fvalue);
11112                     cell.className = " fc-state-disabled";
11113                 }
11114             }
11115             
11116             if (!cell.initialClassName) {
11117                 cell.initialClassName = cell.dom.className;
11118             }
11119             
11120             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11121         };
11122
11123         var i = 0;
11124         
11125         for(; i < startingPos; i++) {
11126             textEls[i].innerHTML = (++prevStart);
11127             d.setDate(d.getDate()+1);
11128             
11129             cells[i].className = "fc-past fc-other-month";
11130             setCellClass(this, cells[i]);
11131         }
11132         
11133         var intDay = 0;
11134         
11135         for(; i < days; i++){
11136             intDay = i - startingPos + 1;
11137             textEls[i].innerHTML = (intDay);
11138             d.setDate(d.getDate()+1);
11139             
11140             cells[i].className = ''; // "x-date-active";
11141             setCellClass(this, cells[i]);
11142         }
11143         var extraDays = 0;
11144         
11145         for(; i < 42; i++) {
11146             textEls[i].innerHTML = (++extraDays);
11147             d.setDate(d.getDate()+1);
11148             
11149             cells[i].className = "fc-future fc-other-month";
11150             setCellClass(this, cells[i]);
11151         }
11152         
11153         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11154         
11155         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11156         
11157         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11158         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11159         
11160         if(totalRows != 6){
11161             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11162             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11163         }
11164         
11165         this.fireEvent('monthchange', this, date);
11166         
11167         
11168         /*
11169         if(!this.internalRender){
11170             var main = this.el.dom.firstChild;
11171             var w = main.offsetWidth;
11172             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11173             Roo.fly(main).setWidth(w);
11174             this.internalRender = true;
11175             // opera does not respect the auto grow header center column
11176             // then, after it gets a width opera refuses to recalculate
11177             // without a second pass
11178             if(Roo.isOpera && !this.secondPass){
11179                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11180                 this.secondPass = true;
11181                 this.update.defer(10, this, [date]);
11182             }
11183         }
11184         */
11185         
11186     },
11187     
11188     findCell : function(dt) {
11189         dt = dt.clearTime().getTime();
11190         var ret = false;
11191         this.cells.each(function(c){
11192             //Roo.log("check " +c.dateValue + '?=' + dt);
11193             if(c.dateValue == dt){
11194                 ret = c;
11195                 return false;
11196             }
11197             return true;
11198         });
11199         
11200         return ret;
11201     },
11202     
11203     findCells : function(ev) {
11204         var s = ev.start.clone().clearTime().getTime();
11205        // Roo.log(s);
11206         var e= ev.end.clone().clearTime().getTime();
11207        // Roo.log(e);
11208         var ret = [];
11209         this.cells.each(function(c){
11210              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11211             
11212             if(c.dateValue > e){
11213                 return ;
11214             }
11215             if(c.dateValue < s){
11216                 return ;
11217             }
11218             ret.push(c);
11219         });
11220         
11221         return ret;    
11222     },
11223     
11224     findBestRow: function(cells)
11225     {
11226         var ret = 0;
11227         
11228         for (var i =0 ; i < cells.length;i++) {
11229             ret  = Math.max(cells[i].rows || 0,ret);
11230         }
11231         return ret;
11232         
11233     },
11234     
11235     
11236     addItem : function(ev)
11237     {
11238         // look for vertical location slot in
11239         var cells = this.findCells(ev);
11240         
11241         ev.row = this.findBestRow(cells);
11242         
11243         // work out the location.
11244         
11245         var crow = false;
11246         var rows = [];
11247         for(var i =0; i < cells.length; i++) {
11248             if (!crow) {
11249                 crow = {
11250                     start : cells[i],
11251                     end :  cells[i]
11252                 };
11253                 continue;
11254             }
11255             if (crow.start.getY() == cells[i].getY()) {
11256                 // on same row.
11257                 crow.end = cells[i];
11258                 continue;
11259             }
11260             // different row.
11261             rows.push(crow);
11262             crow = {
11263                 start: cells[i],
11264                 end : cells[i]
11265             };
11266             
11267         }
11268         
11269         rows.push(crow);
11270         ev.els = [];
11271         ev.rows = rows;
11272         ev.cells = cells;
11273         for (var i = 0; i < cells.length;i++) {
11274             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11275             
11276         }
11277         
11278         this.calevents.push(ev);
11279     },
11280     
11281     clearEvents: function() {
11282         
11283         if(!this.calevents){
11284             return;
11285         }
11286         
11287         Roo.each(this.cells.elements, function(c){
11288             c.rows = 0;
11289         });
11290         
11291         Roo.each(this.calevents, function(e) {
11292             Roo.each(e.els, function(el) {
11293                 el.un('mouseenter' ,this.onEventEnter, this);
11294                 el.un('mouseleave' ,this.onEventLeave, this);
11295                 el.remove();
11296             },this);
11297         },this);
11298         
11299     },
11300     
11301     renderEvents: function()
11302     {   
11303         // first make sure there is enough space..
11304         
11305         this.cells.each(function(c) {
11306         
11307             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11308         });
11309         
11310         for (var e = 0; e < this.calevents.length; e++) {
11311             var ev = this.calevents[e];
11312             var cells = ev.cells;
11313             var rows = ev.rows;
11314             
11315             for(var i =0; i < rows.length; i++) {
11316                 
11317                  
11318                 // how many rows should it span..
11319                 
11320                 var  cfg = {
11321                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11322                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11323                     
11324                     unselectable : "on",
11325                     cn : [
11326                         {
11327                             cls: 'fc-event-inner',
11328                             cn : [
11329 //                                {
11330 //                                  tag:'span',
11331 //                                  cls: 'fc-event-time',
11332 //                                  html : cells.length > 1 ? '' : ev.time
11333 //                                },
11334                                 {
11335                                   tag:'span',
11336                                   cls: 'fc-event-title',
11337                                   html : String.format('{0}', ev.title)
11338                                 }
11339                                 
11340                                 
11341                             ]
11342                         },
11343                         {
11344                             cls: 'ui-resizable-handle ui-resizable-e',
11345                             html : '&nbsp;&nbsp;&nbsp'
11346                         }
11347                         
11348                     ]
11349                 };
11350                 if (i == 0) {
11351                     cfg.cls += ' fc-event-start';
11352                 }
11353                 if ((i+1) == rows.length) {
11354                     cfg.cls += ' fc-event-end';
11355                 }
11356                 
11357                 var ctr = this.el.select('.fc-event-container',true).first();
11358                 var cg = ctr.createChild(cfg);
11359                 
11360                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11361                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11362                 cg.on('click', this.onEventClick, this, ev);
11363                 
11364                 ev.els.push(cg);
11365                 
11366                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11367                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11368                 //Roo.log(cg);
11369                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11370                 cg.setWidth(ebox.right - sbox.x -2);
11371             }
11372             
11373             
11374         }
11375         
11376     },
11377     
11378     onEventEnter: function (e, el,event,d) {
11379         this.fireEvent('evententer', this, el, event);
11380     },
11381     
11382     onEventLeave: function (e, el,event,d) {
11383         this.fireEvent('eventleave', this, el, event);
11384     },
11385     
11386     onEventClick: function (e, el,event,d) {
11387         this.fireEvent('eventclick', this, el, event);
11388     },
11389     
11390     onMonthChange: function () {
11391         this.store.load();
11392     },
11393     
11394     onLoad: function () 
11395     {   
11396         this.calevents = [];
11397         var cal = this;
11398         
11399         if(this.store.getCount() > 0){
11400             this.store.data.each(function(d){
11401                cal.addItem({
11402                     id : d.data.id,
11403                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11404                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11405                     time : d.data.start_time,
11406                     title : d.data.title,
11407                     description : d.data.description,
11408                     venue : d.data.venue
11409                 });
11410             });
11411         }
11412         
11413         this.renderEvents();
11414         
11415         if(this.loadMask){
11416             this.maskEl.hide();
11417         }
11418     },
11419     
11420     onBeforeLoad: function()
11421     {
11422         this.clearEvents();
11423         
11424         if(this.loadMask){
11425             this.maskEl.show();
11426         }
11427     }
11428 });
11429
11430  
11431  /*
11432  * - LGPL
11433  *
11434  * element
11435  * 
11436  */
11437
11438 /**
11439  * @class Roo.bootstrap.Popover
11440  * @extends Roo.bootstrap.Component
11441  * Bootstrap Popover class
11442  * @cfg {String} html contents of the popover   (or false to use children..)
11443  * @cfg {String} title of popover (or false to hide)
11444  * @cfg {String} placement how it is placed
11445  * @cfg {String} trigger click || hover (or false to trigger manually)
11446  * @cfg {String} over what (parent or false to trigger manually.)
11447  * 
11448  * @constructor
11449  * Create a new Popover
11450  * @param {Object} config The config object
11451  */
11452
11453 Roo.bootstrap.Popover = function(config){
11454     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11455 };
11456
11457 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11458     
11459     title: 'Fill in a title',
11460     html: false,
11461     
11462     placement : 'right',
11463     trigger : 'hover', // hover
11464     
11465     over: 'parent',
11466     
11467     can_build_overlaid : false,
11468     
11469     getChildContainer : function()
11470     {
11471         return this.el.select('.popover-content',true).first();
11472     },
11473     
11474     getAutoCreate : function(){
11475          Roo.log('make popover?');
11476         var cfg = {
11477            cls : 'popover roo-dynamic',
11478            style: 'display:block',
11479            cn : [
11480                 {
11481                     cls : 'arrow'
11482                 },
11483                 {
11484                     cls : 'popover-inner',
11485                     cn : [
11486                         {
11487                             tag: 'h3',
11488                             cls: 'popover-title',
11489                             html : this.title
11490                         },
11491                         {
11492                             cls : 'popover-content',
11493                             html : this.html
11494                         }
11495                     ]
11496                     
11497                 }
11498            ]
11499         };
11500         
11501         return cfg;
11502     },
11503     setTitle: function(str)
11504     {
11505         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11506     },
11507     setContent: function(str)
11508     {
11509         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11510     },
11511     // as it get's added to the bottom of the page.
11512     onRender : function(ct, position)
11513     {
11514         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11515         if(!this.el){
11516             var cfg = Roo.apply({},  this.getAutoCreate());
11517             cfg.id = Roo.id();
11518             
11519             if (this.cls) {
11520                 cfg.cls += ' ' + this.cls;
11521             }
11522             if (this.style) {
11523                 cfg.style = this.style;
11524             }
11525             Roo.log("adding to ")
11526             this.el = Roo.get(document.body).createChild(cfg, position);
11527             Roo.log(this.el);
11528         }
11529         this.initEvents();
11530     },
11531     
11532     initEvents : function()
11533     {
11534         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11535         this.el.enableDisplayMode('block');
11536         this.el.hide();
11537         if (this.over === false) {
11538             return; 
11539         }
11540         if (this.triggers === false) {
11541             return;
11542         }
11543         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11544         var triggers = this.trigger ? this.trigger.split(' ') : [];
11545         Roo.each(triggers, function(trigger) {
11546         
11547             if (trigger == 'click') {
11548                 on_el.on('click', this.toggle, this);
11549             } else if (trigger != 'manual') {
11550                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11551                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11552       
11553                 on_el.on(eventIn  ,this.enter, this);
11554                 on_el.on(eventOut, this.leave, this);
11555             }
11556         }, this);
11557         
11558     },
11559     
11560     
11561     // private
11562     timeout : null,
11563     hoverState : null,
11564     
11565     toggle : function () {
11566         this.hoverState == 'in' ? this.leave() : this.enter();
11567     },
11568     
11569     enter : function () {
11570        
11571     
11572         clearTimeout(this.timeout);
11573     
11574         this.hoverState = 'in'
11575     
11576         if (!this.delay || !this.delay.show) {
11577             this.show();
11578             return 
11579         }
11580         var _t = this;
11581         this.timeout = setTimeout(function () {
11582             if (_t.hoverState == 'in') {
11583                 _t.show();
11584             }
11585         }, this.delay.show)
11586     },
11587     leave : function() {
11588         clearTimeout(this.timeout);
11589     
11590         this.hoverState = 'out'
11591     
11592         if (!this.delay || !this.delay.hide) {
11593             this.hide();
11594             return 
11595         }
11596         var _t = this;
11597         this.timeout = setTimeout(function () {
11598             if (_t.hoverState == 'out') {
11599                 _t.hide();
11600             }
11601         }, this.delay.hide)
11602     },
11603     
11604     show : function (on_el)
11605     {
11606         if (!on_el) {
11607             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11608         }
11609         // set content.
11610         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11611         if (this.html !== false) {
11612             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11613         }
11614         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11615         if (!this.title.length) {
11616             this.el.select('.popover-title',true).hide();
11617         }
11618         
11619         var placement = typeof this.placement == 'function' ?
11620             this.placement.call(this, this.el, on_el) :
11621             this.placement;
11622             
11623         var autoToken = /\s?auto?\s?/i;
11624         var autoPlace = autoToken.test(placement);
11625         if (autoPlace) {
11626             placement = placement.replace(autoToken, '') || 'top';
11627         }
11628         
11629         //this.el.detach()
11630         //this.el.setXY([0,0]);
11631         this.el.show();
11632         this.el.dom.style.display='block';
11633         this.el.addClass(placement);
11634         
11635         //this.el.appendTo(on_el);
11636         
11637         var p = this.getPosition();
11638         var box = this.el.getBox();
11639         
11640         if (autoPlace) {
11641             // fixme..
11642         }
11643         var align = Roo.bootstrap.Popover.alignment[placement]
11644         this.el.alignTo(on_el, align[0],align[1]);
11645         //var arrow = this.el.select('.arrow',true).first();
11646         //arrow.set(align[2], 
11647         
11648         this.el.addClass('in');
11649         this.hoverState = null;
11650         
11651         if (this.el.hasClass('fade')) {
11652             // fade it?
11653         }
11654         
11655     },
11656     hide : function()
11657     {
11658         this.el.setXY([0,0]);
11659         this.el.removeClass('in');
11660         this.el.hide();
11661         
11662     }
11663     
11664 });
11665
11666 Roo.bootstrap.Popover.alignment = {
11667     'left' : ['r-l', [-10,0], 'right'],
11668     'right' : ['l-r', [10,0], 'left'],
11669     'bottom' : ['t-b', [0,10], 'top'],
11670     'top' : [ 'b-t', [0,-10], 'bottom']
11671 };
11672
11673  /*
11674  * - LGPL
11675  *
11676  * Progress
11677  * 
11678  */
11679
11680 /**
11681  * @class Roo.bootstrap.Progress
11682  * @extends Roo.bootstrap.Component
11683  * Bootstrap Progress class
11684  * @cfg {Boolean} striped striped of the progress bar
11685  * @cfg {Boolean} active animated of the progress bar
11686  * 
11687  * 
11688  * @constructor
11689  * Create a new Progress
11690  * @param {Object} config The config object
11691  */
11692
11693 Roo.bootstrap.Progress = function(config){
11694     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11695 };
11696
11697 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
11698     
11699     striped : false,
11700     active: false,
11701     
11702     getAutoCreate : function(){
11703         var cfg = {
11704             tag: 'div',
11705             cls: 'progress'
11706         };
11707         
11708         
11709         if(this.striped){
11710             cfg.cls += ' progress-striped';
11711         }
11712       
11713         if(this.active){
11714             cfg.cls += ' active';
11715         }
11716         
11717         
11718         return cfg;
11719     }
11720    
11721 });
11722
11723  
11724
11725  /*
11726  * - LGPL
11727  *
11728  * ProgressBar
11729  * 
11730  */
11731
11732 /**
11733  * @class Roo.bootstrap.ProgressBar
11734  * @extends Roo.bootstrap.Component
11735  * Bootstrap ProgressBar class
11736  * @cfg {Number} aria_valuenow aria-value now
11737  * @cfg {Number} aria_valuemin aria-value min
11738  * @cfg {Number} aria_valuemax aria-value max
11739  * @cfg {String} label label for the progress bar
11740  * @cfg {String} panel (success | info | warning | danger )
11741  * @cfg {String} role role of the progress bar
11742  * @cfg {String} sr_only text
11743  * 
11744  * 
11745  * @constructor
11746  * Create a new ProgressBar
11747  * @param {Object} config The config object
11748  */
11749
11750 Roo.bootstrap.ProgressBar = function(config){
11751     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11752 };
11753
11754 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
11755     
11756     aria_valuenow : 0,
11757     aria_valuemin : 0,
11758     aria_valuemax : 100,
11759     label : false,
11760     panel : false,
11761     role : false,
11762     sr_only: false,
11763     
11764     getAutoCreate : function()
11765     {
11766         
11767         var cfg = {
11768             tag: 'div',
11769             cls: 'progress-bar',
11770             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11771         };
11772         
11773         if(this.sr_only){
11774             cfg.cn = {
11775                 tag: 'span',
11776                 cls: 'sr-only',
11777                 html: this.sr_only
11778             }
11779         }
11780         
11781         if(this.role){
11782             cfg.role = this.role;
11783         }
11784         
11785         if(this.aria_valuenow){
11786             cfg['aria-valuenow'] = this.aria_valuenow;
11787         }
11788         
11789         if(this.aria_valuemin){
11790             cfg['aria-valuemin'] = this.aria_valuemin;
11791         }
11792         
11793         if(this.aria_valuemax){
11794             cfg['aria-valuemax'] = this.aria_valuemax;
11795         }
11796         
11797         if(this.label && !this.sr_only){
11798             cfg.html = this.label;
11799         }
11800         
11801         if(this.panel){
11802             cfg.cls += ' progress-bar-' + this.panel;
11803         }
11804         
11805         return cfg;
11806     },
11807     
11808     update : function(aria_valuenow)
11809     {
11810         this.aria_valuenow = aria_valuenow;
11811         
11812         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11813     }
11814    
11815 });
11816
11817  
11818
11819  /*
11820  * - LGPL
11821  *
11822  * TabPanel
11823  * 
11824  */
11825
11826 /**
11827  * @class Roo.bootstrap.TabPanel
11828  * @extends Roo.bootstrap.Component
11829  * Bootstrap TabPanel class
11830  * @cfg {Boolean} active panel active
11831  * @cfg {String} html panel content
11832  * @cfg {String} tabId tab relate id
11833  * @cfg {String} navId The navbar which triggers show hide
11834  * 
11835  * 
11836  * @constructor
11837  * Create a new TabPanel
11838  * @param {Object} config The config object
11839  */
11840
11841 Roo.bootstrap.TabPanel = function(config){
11842     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11843      this.addEvents({
11844         /**
11845              * @event changed
11846              * Fires when the active status changes
11847              * @param {Roo.bootstrap.TabPanel} this
11848              * @param {Boolean} state the new state
11849             
11850          */
11851         'changed': true
11852      });
11853 };
11854
11855 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
11856     
11857     active: false,
11858     html: false,
11859     tabId: false,
11860     navId : false,
11861     
11862     getAutoCreate : function(){
11863         var cfg = {
11864             tag: 'div',
11865             cls: 'tab-pane',
11866             html: this.html || ''
11867         };
11868         
11869         if(this.active){
11870             cfg.cls += ' active';
11871         }
11872         
11873         if(this.tabId){
11874             cfg.tabId = this.tabId;
11875         }
11876         
11877         return cfg;
11878     },
11879     onRender : function(ct, position)
11880     {
11881        // Roo.log("Call onRender: " + this.xtype);
11882         
11883         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11884         
11885         if (this.navId && this.tabId) {
11886             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11887             if (!item) {
11888                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
11889             } else {
11890                 item.on('changed', function(item, state) {
11891                     this.setActive(state);
11892                 }, this);
11893             }
11894         }
11895         
11896     },
11897     setActive: function(state)
11898     {
11899         Roo.log("panel - set active " + this.tabId + "=" + state);
11900         
11901         this.active = state;
11902         if (!state) {
11903             this.el.removeClass('active');
11904             
11905         } else  if (!this.el.hasClass('active')) {
11906             this.el.addClass('active');
11907         }
11908         this.fireEvent('changed', this, state);
11909     }
11910     
11911     
11912 });
11913  
11914
11915  
11916
11917  /*
11918  * - LGPL
11919  *
11920  * DateField
11921  * 
11922  */
11923
11924 /**
11925  * @class Roo.bootstrap.DateField
11926  * @extends Roo.bootstrap.Input
11927  * Bootstrap DateField class
11928  * @cfg {Number} weekStart default 0
11929  * @cfg {Number} weekStart default 0
11930  * @cfg {Number} viewMode default empty, (months|years)
11931  * @cfg {Number} minViewMode default empty, (months|years)
11932  * @cfg {Number} startDate default -Infinity
11933  * @cfg {Number} endDate default Infinity
11934  * @cfg {Boolean} todayHighlight default false
11935  * @cfg {Boolean} todayBtn default false
11936  * @cfg {Boolean} calendarWeeks default false
11937  * @cfg {Object} daysOfWeekDisabled default empty
11938  * 
11939  * @cfg {Boolean} keyboardNavigation default true
11940  * @cfg {String} language default en
11941  * 
11942  * @constructor
11943  * Create a new DateField
11944  * @param {Object} config The config object
11945  */
11946
11947 Roo.bootstrap.DateField = function(config){
11948     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11949      this.addEvents({
11950             /**
11951              * @event show
11952              * Fires when this field show.
11953              * @param {Roo.bootstrap.DateField} this
11954              * @param {Mixed} date The date value
11955              */
11956             show : true,
11957             /**
11958              * @event show
11959              * Fires when this field hide.
11960              * @param {Roo.bootstrap.DateField} this
11961              * @param {Mixed} date The date value
11962              */
11963             hide : true,
11964             /**
11965              * @event select
11966              * Fires when select a date.
11967              * @param {Roo.bootstrap.DateField} this
11968              * @param {Mixed} date The date value
11969              */
11970             select : true
11971         });
11972 };
11973
11974 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11975     
11976     /**
11977      * @cfg {String} format
11978      * The default date format string which can be overriden for localization support.  The format must be
11979      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11980      */
11981     format : "m/d/y",
11982     /**
11983      * @cfg {String} altFormats
11984      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11985      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11986      */
11987     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11988     
11989     weekStart : 0,
11990     
11991     viewMode : '',
11992     
11993     minViewMode : '',
11994     
11995     todayHighlight : false,
11996     
11997     todayBtn: false,
11998     
11999     language: 'en',
12000     
12001     keyboardNavigation: true,
12002     
12003     calendarWeeks: false,
12004     
12005     startDate: -Infinity,
12006     
12007     endDate: Infinity,
12008     
12009     daysOfWeekDisabled: [],
12010     
12011     _events: [],
12012     
12013     UTCDate: function()
12014     {
12015         return new Date(Date.UTC.apply(Date, arguments));
12016     },
12017     
12018     UTCToday: function()
12019     {
12020         var today = new Date();
12021         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12022     },
12023     
12024     getDate: function() {
12025             var d = this.getUTCDate();
12026             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12027     },
12028     
12029     getUTCDate: function() {
12030             return this.date;
12031     },
12032     
12033     setDate: function(d) {
12034             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12035     },
12036     
12037     setUTCDate: function(d) {
12038             this.date = d;
12039             this.setValue(this.formatDate(this.date));
12040     },
12041         
12042     onRender: function(ct, position)
12043     {
12044         
12045         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12046         
12047         this.language = this.language || 'en';
12048         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12049         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12050         
12051         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12052         this.format = this.format || 'm/d/y';
12053         this.isInline = false;
12054         this.isInput = true;
12055         this.component = this.el.select('.add-on', true).first() || false;
12056         this.component = (this.component && this.component.length === 0) ? false : this.component;
12057         this.hasInput = this.component && this.inputEL().length;
12058         
12059         if (typeof(this.minViewMode === 'string')) {
12060             switch (this.minViewMode) {
12061                 case 'months':
12062                     this.minViewMode = 1;
12063                     break;
12064                 case 'years':
12065                     this.minViewMode = 2;
12066                     break;
12067                 default:
12068                     this.minViewMode = 0;
12069                     break;
12070             }
12071         }
12072         
12073         if (typeof(this.viewMode === 'string')) {
12074             switch (this.viewMode) {
12075                 case 'months':
12076                     this.viewMode = 1;
12077                     break;
12078                 case 'years':
12079                     this.viewMode = 2;
12080                     break;
12081                 default:
12082                     this.viewMode = 0;
12083                     break;
12084             }
12085         }
12086                 
12087         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12088         
12089         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12090         
12091         this.picker().on('mousedown', this.onMousedown, this);
12092         this.picker().on('click', this.onClick, this);
12093         
12094         this.picker().addClass('datepicker-dropdown');
12095         
12096         this.startViewMode = this.viewMode;
12097         
12098         
12099         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12100             if(!this.calendarWeeks){
12101                 v.remove();
12102                 return;
12103             };
12104             
12105             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12106             v.attr('colspan', function(i, val){
12107                 return parseInt(val) + 1;
12108             });
12109         })
12110                         
12111         
12112         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12113         
12114         this.setStartDate(this.startDate);
12115         this.setEndDate(this.endDate);
12116         
12117         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12118         
12119         this.fillDow();
12120         this.fillMonths();
12121         this.update();
12122         this.showMode();
12123         
12124         if(this.isInline) {
12125             this.show();
12126         }
12127     },
12128     
12129     picker : function()
12130     {
12131         return this.el.select('.datepicker', true).first();
12132     },
12133     
12134     fillDow: function()
12135     {
12136         var dowCnt = this.weekStart;
12137         
12138         var dow = {
12139             tag: 'tr',
12140             cn: [
12141                 
12142             ]
12143         };
12144         
12145         if(this.calendarWeeks){
12146             dow.cn.push({
12147                 tag: 'th',
12148                 cls: 'cw',
12149                 html: '&nbsp;'
12150             })
12151         }
12152         
12153         while (dowCnt < this.weekStart + 7) {
12154             dow.cn.push({
12155                 tag: 'th',
12156                 cls: 'dow',
12157                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12158             });
12159         }
12160         
12161         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12162     },
12163     
12164     fillMonths: function()
12165     {    
12166         var i = 0
12167         var months = this.picker().select('>.datepicker-months td', true).first();
12168         
12169         months.dom.innerHTML = '';
12170         
12171         while (i < 12) {
12172             var month = {
12173                 tag: 'span',
12174                 cls: 'month',
12175                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12176             }
12177             
12178             months.createChild(month);
12179         }
12180         
12181     },
12182     
12183     update: function(){
12184         
12185         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12186         
12187         if (this.date < this.startDate) {
12188             this.viewDate = new Date(this.startDate);
12189         } else if (this.date > this.endDate) {
12190             this.viewDate = new Date(this.endDate);
12191         } else {
12192             this.viewDate = new Date(this.date);
12193         }
12194         
12195         this.fill();
12196     },
12197     
12198     fill: function() {
12199         var d = new Date(this.viewDate),
12200                 year = d.getUTCFullYear(),
12201                 month = d.getUTCMonth(),
12202                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12203                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12204                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12205                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12206                 currentDate = this.date && this.date.valueOf(),
12207                 today = this.UTCToday();
12208         
12209         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12210         
12211 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12212         
12213 //        this.picker.select('>tfoot th.today').
12214 //                                              .text(dates[this.language].today)
12215 //                                              .toggle(this.todayBtn !== false);
12216     
12217         this.updateNavArrows();
12218         this.fillMonths();
12219                                                 
12220         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12221         
12222         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12223          
12224         prevMonth.setUTCDate(day);
12225         
12226         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12227         
12228         var nextMonth = new Date(prevMonth);
12229         
12230         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12231         
12232         nextMonth = nextMonth.valueOf();
12233         
12234         var fillMonths = false;
12235         
12236         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12237         
12238         while(prevMonth.valueOf() < nextMonth) {
12239             var clsName = '';
12240             
12241             if (prevMonth.getUTCDay() === this.weekStart) {
12242                 if(fillMonths){
12243                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12244                 }
12245                     
12246                 fillMonths = {
12247                     tag: 'tr',
12248                     cn: []
12249                 };
12250                 
12251                 if(this.calendarWeeks){
12252                     // ISO 8601: First week contains first thursday.
12253                     // ISO also states week starts on Monday, but we can be more abstract here.
12254                     var
12255                     // Start of current week: based on weekstart/current date
12256                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12257                     // Thursday of this week
12258                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12259                     // First Thursday of year, year from thursday
12260                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12261                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12262                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12263                     
12264                     fillMonths.cn.push({
12265                         tag: 'td',
12266                         cls: 'cw',
12267                         html: calWeek
12268                     });
12269                 }
12270             }
12271             
12272             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12273                 clsName += ' old';
12274             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12275                 clsName += ' new';
12276             }
12277             if (this.todayHighlight &&
12278                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12279                 prevMonth.getUTCMonth() == today.getMonth() &&
12280                 prevMonth.getUTCDate() == today.getDate()) {
12281                 clsName += ' today';
12282             }
12283             
12284             if (currentDate && prevMonth.valueOf() === currentDate) {
12285                 clsName += ' active';
12286             }
12287             
12288             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12289                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12290                     clsName += ' disabled';
12291             }
12292             
12293             fillMonths.cn.push({
12294                 tag: 'td',
12295                 cls: 'day ' + clsName,
12296                 html: prevMonth.getDate()
12297             })
12298             
12299             prevMonth.setDate(prevMonth.getDate()+1);
12300         }
12301           
12302         var currentYear = this.date && this.date.getUTCFullYear();
12303         var currentMonth = this.date && this.date.getUTCMonth();
12304         
12305         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12306         
12307         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12308             v.removeClass('active');
12309             
12310             if(currentYear === year && k === currentMonth){
12311                 v.addClass('active');
12312             }
12313             
12314             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12315                 v.addClass('disabled');
12316             }
12317             
12318         });
12319         
12320         
12321         year = parseInt(year/10, 10) * 10;
12322         
12323         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12324         
12325         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12326         
12327         year -= 1;
12328         for (var i = -1; i < 11; i++) {
12329             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12330                 tag: 'span',
12331                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12332                 html: year
12333             })
12334             
12335             year += 1;
12336         }
12337     },
12338     
12339     showMode: function(dir) {
12340         if (dir) {
12341             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12342         }
12343         Roo.each(this.picker().select('>div',true).elements, function(v){
12344             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12345             v.hide();
12346         });
12347         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12348     },
12349     
12350     place: function()
12351     {
12352         if(this.isInline) return;
12353         
12354         this.picker().removeClass(['bottom', 'top']);
12355         
12356         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12357             /*
12358              * place to the top of element!
12359              *
12360              */
12361             
12362             this.picker().addClass('top');
12363             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12364             
12365             return;
12366         }
12367         
12368         this.picker().addClass('bottom');
12369         
12370         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12371     },
12372     
12373     parseDate : function(value){
12374         if(!value || value instanceof Date){
12375             return value;
12376         }
12377         var v = Date.parseDate(value, this.format);
12378         if (!v && this.useIso) {
12379             v = Date.parseDate(value, 'Y-m-d');
12380         }
12381         if(!v && this.altFormats){
12382             if(!this.altFormatsArray){
12383                 this.altFormatsArray = this.altFormats.split("|");
12384             }
12385             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12386                 v = Date.parseDate(value, this.altFormatsArray[i]);
12387             }
12388         }
12389         return v;
12390     },
12391     
12392     formatDate : function(date, fmt){
12393         return (!date || !(date instanceof Date)) ?
12394         date : date.dateFormat(fmt || this.format);
12395     },
12396     
12397     onFocus : function()
12398     {
12399         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12400         this.show();
12401     },
12402     
12403     onBlur : function()
12404     {
12405         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12406         this.hide();
12407     },
12408     
12409     show : function()
12410     {
12411         this.picker().show();
12412         this.update();
12413         this.place();
12414         
12415         this.fireEvent('show', this, this.date);
12416     },
12417     
12418     hide : function()
12419     {
12420         if(this.isInline) return;
12421         this.picker().hide();
12422         this.viewMode = this.startViewMode;
12423         this.showMode();
12424         
12425         this.fireEvent('hide', this, this.date);
12426         
12427     },
12428     
12429     onMousedown: function(e){
12430         e.stopPropagation();
12431         e.preventDefault();
12432     },
12433     
12434     keyup: function(e){
12435         Roo.bootstrap.DateField.superclass.keyup.call(this);
12436         this.update();
12437         
12438     },
12439
12440     setValue: function(v){
12441         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12442         
12443         this.fireEvent('select', this, this.date);
12444         
12445     },
12446     
12447     fireKey: function(e){
12448         if (!this.picker().isVisible()){
12449             if (e.keyCode == 27) // allow escape to hide and re-show picker
12450                 this.show();
12451             return;
12452         }
12453         var dateChanged = false,
12454         dir, day, month,
12455         newDate, newViewDate;
12456         switch(e.keyCode){
12457             case 27: // escape
12458                 this.hide();
12459                 e.preventDefault();
12460                 break;
12461             case 37: // left
12462             case 39: // right
12463                 if (!this.keyboardNavigation) break;
12464                 dir = e.keyCode == 37 ? -1 : 1;
12465                 
12466                 if (e.ctrlKey){
12467                     newDate = this.moveYear(this.date, dir);
12468                     newViewDate = this.moveYear(this.viewDate, dir);
12469                 } else if (e.shiftKey){
12470                     newDate = this.moveMonth(this.date, dir);
12471                     newViewDate = this.moveMonth(this.viewDate, dir);
12472                 } else {
12473                     newDate = new Date(this.date);
12474                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12475                     newViewDate = new Date(this.viewDate);
12476                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12477                 }
12478                 if (this.dateWithinRange(newDate)){
12479                     this.date = newDate;
12480                     this.viewDate = newViewDate;
12481                     this.setValue(this.formatDate(this.date));
12482                     this.update();
12483                     e.preventDefault();
12484                     dateChanged = true;
12485                 }
12486                 break;
12487             case 38: // up
12488             case 40: // down
12489                 if (!this.keyboardNavigation) break;
12490                 dir = e.keyCode == 38 ? -1 : 1;
12491                 if (e.ctrlKey){
12492                     newDate = this.moveYear(this.date, dir);
12493                     newViewDate = this.moveYear(this.viewDate, dir);
12494                 } else if (e.shiftKey){
12495                     newDate = this.moveMonth(this.date, dir);
12496                     newViewDate = this.moveMonth(this.viewDate, dir);
12497                 } else {
12498                     newDate = new Date(this.date);
12499                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12500                     newViewDate = new Date(this.viewDate);
12501                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12502                 }
12503                 if (this.dateWithinRange(newDate)){
12504                     this.date = newDate;
12505                     this.viewDate = newViewDate;
12506                     this.setValue(this.formatDate(this.date));
12507                     this.update();
12508                     e.preventDefault();
12509                     dateChanged = true;
12510                 }
12511                 break;
12512             case 13: // enter
12513                 this.setValue(this.formatDate(this.date));
12514                 this.hide();
12515                 e.preventDefault();
12516                 break;
12517             case 9: // tab
12518                 this.setValue(this.formatDate(this.date));
12519                 this.hide();
12520                 break;
12521         }
12522     },
12523     
12524     
12525     onClick: function(e) {
12526         e.stopPropagation();
12527         e.preventDefault();
12528         
12529         var target = e.getTarget();
12530         
12531         if(target.nodeName.toLowerCase() === 'i'){
12532             target = Roo.get(target).dom.parentNode;
12533         }
12534         
12535         var nodeName = target.nodeName;
12536         var className = target.className;
12537         var html = target.innerHTML;
12538         
12539         switch(nodeName.toLowerCase()) {
12540             case 'th':
12541                 switch(className) {
12542                     case 'switch':
12543                         this.showMode(1);
12544                         break;
12545                     case 'prev':
12546                     case 'next':
12547                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12548                         switch(this.viewMode){
12549                                 case 0:
12550                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12551                                         break;
12552                                 case 1:
12553                                 case 2:
12554                                         this.viewDate = this.moveYear(this.viewDate, dir);
12555                                         break;
12556                         }
12557                         this.fill();
12558                         break;
12559                     case 'today':
12560                         var date = new Date();
12561                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12562                         this.fill()
12563                         this.setValue(this.formatDate(this.date));
12564                         this.hide();
12565                         break;
12566                 }
12567                 break;
12568             case 'span':
12569                 if (className.indexOf('disabled') === -1) {
12570                     this.viewDate.setUTCDate(1);
12571                     if (className.indexOf('month') !== -1) {
12572                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12573                     } else {
12574                         var year = parseInt(html, 10) || 0;
12575                         this.viewDate.setUTCFullYear(year);
12576                         
12577                     }
12578                     this.showMode(-1);
12579                     this.fill();
12580                 }
12581                 break;
12582                 
12583             case 'td':
12584                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12585                     var day = parseInt(html, 10) || 1;
12586                     var year = this.viewDate.getUTCFullYear(),
12587                         month = this.viewDate.getUTCMonth();
12588
12589                     if (className.indexOf('old') !== -1) {
12590                         if(month === 0 ){
12591                             month = 11;
12592                             year -= 1;
12593                         }else{
12594                             month -= 1;
12595                         }
12596                     } else if (className.indexOf('new') !== -1) {
12597                         if (month == 11) {
12598                             month = 0;
12599                             year += 1;
12600                         } else {
12601                             month += 1;
12602                         }
12603                     }
12604                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12605                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12606                     this.fill();
12607                     this.setValue(this.formatDate(this.date));
12608                     this.hide();
12609                 }
12610                 break;
12611         }
12612     },
12613     
12614     setStartDate: function(startDate){
12615         this.startDate = startDate || -Infinity;
12616         if (this.startDate !== -Infinity) {
12617             this.startDate = this.parseDate(this.startDate);
12618         }
12619         this.update();
12620         this.updateNavArrows();
12621     },
12622
12623     setEndDate: function(endDate){
12624         this.endDate = endDate || Infinity;
12625         if (this.endDate !== Infinity) {
12626             this.endDate = this.parseDate(this.endDate);
12627         }
12628         this.update();
12629         this.updateNavArrows();
12630     },
12631     
12632     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12633         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12634         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12635             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12636         }
12637         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12638             return parseInt(d, 10);
12639         });
12640         this.update();
12641         this.updateNavArrows();
12642     },
12643     
12644     updateNavArrows: function() {
12645         var d = new Date(this.viewDate),
12646         year = d.getUTCFullYear(),
12647         month = d.getUTCMonth();
12648         
12649         Roo.each(this.picker().select('.prev', true).elements, function(v){
12650             v.show();
12651             switch (this.viewMode) {
12652                 case 0:
12653
12654                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12655                         v.hide();
12656                     }
12657                     break;
12658                 case 1:
12659                 case 2:
12660                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12661                         v.hide();
12662                     }
12663                     break;
12664             }
12665         });
12666         
12667         Roo.each(this.picker().select('.next', true).elements, function(v){
12668             v.show();
12669             switch (this.viewMode) {
12670                 case 0:
12671
12672                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12673                         v.hide();
12674                     }
12675                     break;
12676                 case 1:
12677                 case 2:
12678                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12679                         v.hide();
12680                     }
12681                     break;
12682             }
12683         })
12684     },
12685     
12686     moveMonth: function(date, dir){
12687         if (!dir) return date;
12688         var new_date = new Date(date.valueOf()),
12689         day = new_date.getUTCDate(),
12690         month = new_date.getUTCMonth(),
12691         mag = Math.abs(dir),
12692         new_month, test;
12693         dir = dir > 0 ? 1 : -1;
12694         if (mag == 1){
12695             test = dir == -1
12696             // If going back one month, make sure month is not current month
12697             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12698             ? function(){
12699                 return new_date.getUTCMonth() == month;
12700             }
12701             // If going forward one month, make sure month is as expected
12702             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12703             : function(){
12704                 return new_date.getUTCMonth() != new_month;
12705             };
12706             new_month = month + dir;
12707             new_date.setUTCMonth(new_month);
12708             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12709             if (new_month < 0 || new_month > 11)
12710                 new_month = (new_month + 12) % 12;
12711         } else {
12712             // For magnitudes >1, move one month at a time...
12713             for (var i=0; i<mag; i++)
12714                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12715                 new_date = this.moveMonth(new_date, dir);
12716             // ...then reset the day, keeping it in the new month
12717             new_month = new_date.getUTCMonth();
12718             new_date.setUTCDate(day);
12719             test = function(){
12720                 return new_month != new_date.getUTCMonth();
12721             };
12722         }
12723         // Common date-resetting loop -- if date is beyond end of month, make it
12724         // end of month
12725         while (test()){
12726             new_date.setUTCDate(--day);
12727             new_date.setUTCMonth(new_month);
12728         }
12729         return new_date;
12730     },
12731
12732     moveYear: function(date, dir){
12733         return this.moveMonth(date, dir*12);
12734     },
12735
12736     dateWithinRange: function(date){
12737         return date >= this.startDate && date <= this.endDate;
12738     },
12739
12740     
12741     remove: function() {
12742         this.picker().remove();
12743     }
12744    
12745 });
12746
12747 Roo.apply(Roo.bootstrap.DateField,  {
12748     
12749     head : {
12750         tag: 'thead',
12751         cn: [
12752         {
12753             tag: 'tr',
12754             cn: [
12755             {
12756                 tag: 'th',
12757                 cls: 'prev',
12758                 html: '<i class="icon-arrow-left"/>'
12759             },
12760             {
12761                 tag: 'th',
12762                 cls: 'switch',
12763                 colspan: '5'
12764             },
12765             {
12766                 tag: 'th',
12767                 cls: 'next',
12768                 html: '<i class="icon-arrow-right"/>'
12769             }
12770
12771             ]
12772         }
12773         ]
12774     },
12775     
12776     content : {
12777         tag: 'tbody',
12778         cn: [
12779         {
12780             tag: 'tr',
12781             cn: [
12782             {
12783                 tag: 'td',
12784                 colspan: '7'
12785             }
12786             ]
12787         }
12788         ]
12789     },
12790     
12791     footer : {
12792         tag: 'tfoot',
12793         cn: [
12794         {
12795             tag: 'tr',
12796             cn: [
12797             {
12798                 tag: 'th',
12799                 colspan: '7',
12800                 cls: 'today'
12801             }
12802                     
12803             ]
12804         }
12805         ]
12806     },
12807     
12808     dates:{
12809         en: {
12810             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12811             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12812             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12813             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12814             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12815             today: "Today"
12816         }
12817     },
12818     
12819     modes: [
12820     {
12821         clsName: 'days',
12822         navFnc: 'Month',
12823         navStep: 1
12824     },
12825     {
12826         clsName: 'months',
12827         navFnc: 'FullYear',
12828         navStep: 1
12829     },
12830     {
12831         clsName: 'years',
12832         navFnc: 'FullYear',
12833         navStep: 10
12834     }]
12835 });
12836
12837 Roo.apply(Roo.bootstrap.DateField,  {
12838   
12839     template : {
12840         tag: 'div',
12841         cls: 'datepicker dropdown-menu',
12842         cn: [
12843         {
12844             tag: 'div',
12845             cls: 'datepicker-days',
12846             cn: [
12847             {
12848                 tag: 'table',
12849                 cls: 'table-condensed',
12850                 cn:[
12851                 Roo.bootstrap.DateField.head,
12852                 {
12853                     tag: 'tbody'
12854                 },
12855                 Roo.bootstrap.DateField.footer
12856                 ]
12857             }
12858             ]
12859         },
12860         {
12861             tag: 'div',
12862             cls: 'datepicker-months',
12863             cn: [
12864             {
12865                 tag: 'table',
12866                 cls: 'table-condensed',
12867                 cn:[
12868                 Roo.bootstrap.DateField.head,
12869                 Roo.bootstrap.DateField.content,
12870                 Roo.bootstrap.DateField.footer
12871                 ]
12872             }
12873             ]
12874         },
12875         {
12876             tag: 'div',
12877             cls: 'datepicker-years',
12878             cn: [
12879             {
12880                 tag: 'table',
12881                 cls: 'table-condensed',
12882                 cn:[
12883                 Roo.bootstrap.DateField.head,
12884                 Roo.bootstrap.DateField.content,
12885                 Roo.bootstrap.DateField.footer
12886                 ]
12887             }
12888             ]
12889         }
12890         ]
12891     }
12892 });
12893
12894  
12895
12896  /*
12897  * - LGPL
12898  *
12899  * TimeField
12900  * 
12901  */
12902
12903 /**
12904  * @class Roo.bootstrap.TimeField
12905  * @extends Roo.bootstrap.Input
12906  * Bootstrap DateField class
12907  * 
12908  * 
12909  * @constructor
12910  * Create a new TimeField
12911  * @param {Object} config The config object
12912  */
12913
12914 Roo.bootstrap.TimeField = function(config){
12915     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12916     this.addEvents({
12917             /**
12918              * @event show
12919              * Fires when this field show.
12920              * @param {Roo.bootstrap.DateField} this
12921              * @param {Mixed} date The date value
12922              */
12923             show : true,
12924             /**
12925              * @event show
12926              * Fires when this field hide.
12927              * @param {Roo.bootstrap.DateField} this
12928              * @param {Mixed} date The date value
12929              */
12930             hide : true,
12931             /**
12932              * @event select
12933              * Fires when select a date.
12934              * @param {Roo.bootstrap.DateField} this
12935              * @param {Mixed} date The date value
12936              */
12937             select : true
12938         });
12939 };
12940
12941 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12942     
12943     /**
12944      * @cfg {String} format
12945      * The default time format string which can be overriden for localization support.  The format must be
12946      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12947      */
12948     format : "H:i",
12949        
12950     onRender: function(ct, position)
12951     {
12952         
12953         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12954                 
12955         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12956         
12957         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12958         
12959         this.pop = this.picker().select('>.datepicker-time',true).first();
12960         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12961         
12962         this.picker().on('mousedown', this.onMousedown, this);
12963         this.picker().on('click', this.onClick, this);
12964         
12965         this.picker().addClass('datepicker-dropdown');
12966     
12967         this.fillTime();
12968         this.update();
12969             
12970         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12971         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12972         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12973         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12974         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12975         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12976
12977     },
12978     
12979     fireKey: function(e){
12980         if (!this.picker().isVisible()){
12981             if (e.keyCode == 27) // allow escape to hide and re-show picker
12982                 this.show();
12983             return;
12984         }
12985
12986         e.preventDefault();
12987         
12988         switch(e.keyCode){
12989             case 27: // escape
12990                 this.hide();
12991                 break;
12992             case 37: // left
12993             case 39: // right
12994                 this.onTogglePeriod();
12995                 break;
12996             case 38: // up
12997                 this.onIncrementMinutes();
12998                 break;
12999             case 40: // down
13000                 this.onDecrementMinutes();
13001                 break;
13002             case 13: // enter
13003             case 9: // tab
13004                 this.setTime();
13005                 break;
13006         }
13007     },
13008     
13009     onClick: function(e) {
13010         e.stopPropagation();
13011         e.preventDefault();
13012     },
13013     
13014     picker : function()
13015     {
13016         return this.el.select('.datepicker', true).first();
13017     },
13018     
13019     fillTime: function()
13020     {    
13021         var time = this.pop.select('tbody', true).first();
13022         
13023         time.dom.innerHTML = '';
13024         
13025         time.createChild({
13026             tag: 'tr',
13027             cn: [
13028                 {
13029                     tag: 'td',
13030                     cn: [
13031                         {
13032                             tag: 'a',
13033                             href: '#',
13034                             cls: 'btn',
13035                             cn: [
13036                                 {
13037                                     tag: 'span',
13038                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13039                                 }
13040                             ]
13041                         } 
13042                     ]
13043                 },
13044                 {
13045                     tag: 'td',
13046                     cls: 'separator'
13047                 },
13048                 {
13049                     tag: 'td',
13050                     cn: [
13051                         {
13052                             tag: 'a',
13053                             href: '#',
13054                             cls: 'btn',
13055                             cn: [
13056                                 {
13057                                     tag: 'span',
13058                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13059                                 }
13060                             ]
13061                         }
13062                     ]
13063                 },
13064                 {
13065                     tag: 'td',
13066                     cls: 'separator'
13067                 }
13068             ]
13069         });
13070         
13071         time.createChild({
13072             tag: 'tr',
13073             cn: [
13074                 {
13075                     tag: 'td',
13076                     cn: [
13077                         {
13078                             tag: 'span',
13079                             cls: 'timepicker-hour',
13080                             html: '00'
13081                         }  
13082                     ]
13083                 },
13084                 {
13085                     tag: 'td',
13086                     cls: 'separator',
13087                     html: ':'
13088                 },
13089                 {
13090                     tag: 'td',
13091                     cn: [
13092                         {
13093                             tag: 'span',
13094                             cls: 'timepicker-minute',
13095                             html: '00'
13096                         }  
13097                     ]
13098                 },
13099                 {
13100                     tag: 'td',
13101                     cls: 'separator'
13102                 },
13103                 {
13104                     tag: 'td',
13105                     cn: [
13106                         {
13107                             tag: 'button',
13108                             type: 'button',
13109                             cls: 'btn btn-primary period',
13110                             html: 'AM'
13111                             
13112                         }
13113                     ]
13114                 }
13115             ]
13116         });
13117         
13118         time.createChild({
13119             tag: 'tr',
13120             cn: [
13121                 {
13122                     tag: 'td',
13123                     cn: [
13124                         {
13125                             tag: 'a',
13126                             href: '#',
13127                             cls: 'btn',
13128                             cn: [
13129                                 {
13130                                     tag: 'span',
13131                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13132                                 }
13133                             ]
13134                         }
13135                     ]
13136                 },
13137                 {
13138                     tag: 'td',
13139                     cls: 'separator'
13140                 },
13141                 {
13142                     tag: 'td',
13143                     cn: [
13144                         {
13145                             tag: 'a',
13146                             href: '#',
13147                             cls: 'btn',
13148                             cn: [
13149                                 {
13150                                     tag: 'span',
13151                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13152                                 }
13153                             ]
13154                         }
13155                     ]
13156                 },
13157                 {
13158                     tag: 'td',
13159                     cls: 'separator'
13160                 }
13161             ]
13162         });
13163         
13164     },
13165     
13166     update: function()
13167     {
13168         
13169         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13170         
13171         this.fill();
13172     },
13173     
13174     fill: function() 
13175     {
13176         var hours = this.time.getHours();
13177         var minutes = this.time.getMinutes();
13178         var period = 'AM';
13179         
13180         if(hours > 11){
13181             period = 'PM';
13182         }
13183         
13184         if(hours == 0){
13185             hours = 12;
13186         }
13187         
13188         
13189         if(hours > 12){
13190             hours = hours - 12;
13191         }
13192         
13193         if(hours < 10){
13194             hours = '0' + hours;
13195         }
13196         
13197         if(minutes < 10){
13198             minutes = '0' + minutes;
13199         }
13200         
13201         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13202         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13203         this.pop.select('button', true).first().dom.innerHTML = period;
13204         
13205     },
13206     
13207     place: function()
13208     {   
13209         this.picker().removeClass(['bottom', 'top']);
13210         
13211         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13212             /*
13213              * place to the top of element!
13214              *
13215              */
13216             
13217             this.picker().addClass('top');
13218             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13219             
13220             return;
13221         }
13222         
13223         this.picker().addClass('bottom');
13224         
13225         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13226     },
13227   
13228     onFocus : function()
13229     {
13230         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13231         this.show();
13232     },
13233     
13234     onBlur : function()
13235     {
13236         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13237         this.hide();
13238     },
13239     
13240     show : function()
13241     {
13242         this.picker().show();
13243         this.pop.show();
13244         this.update();
13245         this.place();
13246         
13247         this.fireEvent('show', this, this.date);
13248     },
13249     
13250     hide : function()
13251     {
13252         this.picker().hide();
13253         this.pop.hide();
13254         
13255         this.fireEvent('hide', this, this.date);
13256     },
13257     
13258     setTime : function()
13259     {
13260         this.hide();
13261         this.setValue(this.time.format(this.format));
13262         
13263         this.fireEvent('select', this, this.date);
13264         
13265         
13266     },
13267     
13268     onMousedown: function(e){
13269         e.stopPropagation();
13270         e.preventDefault();
13271     },
13272     
13273     onIncrementHours: function()
13274     {
13275         Roo.log('onIncrementHours');
13276         this.time = this.time.add(Date.HOUR, 1);
13277         this.update();
13278         
13279     },
13280     
13281     onDecrementHours: function()
13282     {
13283         Roo.log('onDecrementHours');
13284         this.time = this.time.add(Date.HOUR, -1);
13285         this.update();
13286     },
13287     
13288     onIncrementMinutes: function()
13289     {
13290         Roo.log('onIncrementMinutes');
13291         this.time = this.time.add(Date.MINUTE, 1);
13292         this.update();
13293     },
13294     
13295     onDecrementMinutes: function()
13296     {
13297         Roo.log('onDecrementMinutes');
13298         this.time = this.time.add(Date.MINUTE, -1);
13299         this.update();
13300     },
13301     
13302     onTogglePeriod: function()
13303     {
13304         Roo.log('onTogglePeriod');
13305         this.time = this.time.add(Date.HOUR, 12);
13306         this.update();
13307     }
13308     
13309    
13310 });
13311
13312 Roo.apply(Roo.bootstrap.TimeField,  {
13313     
13314     content : {
13315         tag: 'tbody',
13316         cn: [
13317             {
13318                 tag: 'tr',
13319                 cn: [
13320                 {
13321                     tag: 'td',
13322                     colspan: '7'
13323                 }
13324                 ]
13325             }
13326         ]
13327     },
13328     
13329     footer : {
13330         tag: 'tfoot',
13331         cn: [
13332             {
13333                 tag: 'tr',
13334                 cn: [
13335                 {
13336                     tag: 'th',
13337                     colspan: '7',
13338                     cls: '',
13339                     cn: [
13340                         {
13341                             tag: 'button',
13342                             cls: 'btn btn-info ok',
13343                             html: 'OK'
13344                         }
13345                     ]
13346                 }
13347
13348                 ]
13349             }
13350         ]
13351     }
13352 });
13353
13354 Roo.apply(Roo.bootstrap.TimeField,  {
13355   
13356     template : {
13357         tag: 'div',
13358         cls: 'datepicker dropdown-menu',
13359         cn: [
13360             {
13361                 tag: 'div',
13362                 cls: 'datepicker-time',
13363                 cn: [
13364                 {
13365                     tag: 'table',
13366                     cls: 'table-condensed',
13367                     cn:[
13368                     Roo.bootstrap.TimeField.content,
13369                     Roo.bootstrap.TimeField.footer
13370                     ]
13371                 }
13372                 ]
13373             }
13374         ]
13375     }
13376 });
13377
13378  
13379
13380  /*
13381  * - LGPL
13382  *
13383  * CheckBox
13384  * 
13385  */
13386
13387 /**
13388  * @class Roo.bootstrap.CheckBox
13389  * @extends Roo.bootstrap.Input
13390  * Bootstrap CheckBox class
13391  * 
13392  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13393  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13394  * @cfg {String} boxLabel The text that appears beside the checkbox
13395  * @cfg {Boolean} checked initnal the element
13396  * 
13397  * @constructor
13398  * Create a new CheckBox
13399  * @param {Object} config The config object
13400  */
13401
13402 Roo.bootstrap.CheckBox = function(config){
13403     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13404    
13405         this.addEvents({
13406             /**
13407             * @event check
13408             * Fires when the element is checked or unchecked.
13409             * @param {Roo.bootstrap.CheckBox} this This input
13410             * @param {Boolean} checked The new checked value
13411             */
13412            check : true
13413         });
13414 };
13415
13416 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13417     
13418     inputType: 'checkbox',
13419     inputValue: 1,
13420     valueOff: 0,
13421     boxLabel: false,
13422     checked: false,
13423     
13424     getAutoCreate : function()
13425     {
13426         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13427         
13428         var id = Roo.id();
13429         
13430         var cfg = {};
13431         
13432         cfg.cls = 'form-group' //input-group
13433         
13434         var input =  {
13435             tag: 'input',
13436             id : id,
13437             type : this.inputType,
13438             value : (!this.checked) ? this.valueOff : this.inputValue,
13439             cls : 'form-box',
13440             placeholder : this.placeholder || ''
13441             
13442         };
13443         
13444         if (this.disabled) {
13445             input.disabled=true;
13446         }
13447         
13448         if(this.checked){
13449             input.checked = this.checked;
13450         }
13451         
13452         if (this.name) {
13453             input.name = this.name;
13454         }
13455         
13456         if (this.size) {
13457             input.cls += ' input-' + this.size;
13458         }
13459         
13460         var settings=this;
13461         ['xs','sm','md','lg'].map(function(size){
13462             if (settings[size]) {
13463                 cfg.cls += ' col-' + size + '-' + settings[size];
13464             }
13465         });
13466         
13467         var inputblock = input;
13468         
13469         if (this.before || this.after) {
13470             
13471             inputblock = {
13472                 cls : 'input-group',
13473                 cn :  [] 
13474             };
13475             if (this.before) {
13476                 inputblock.cn.push({
13477                     tag :'span',
13478                     cls : 'input-group-addon',
13479                     html : this.before
13480                 });
13481             }
13482             inputblock.cn.push(input);
13483             if (this.after) {
13484                 inputblock.cn.push({
13485                     tag :'span',
13486                     cls : 'input-group-addon',
13487                     html : this.after
13488                 });
13489             }
13490             
13491         };
13492         
13493         if (align ==='left' && this.fieldLabel.length) {
13494                 Roo.log("left and has label");
13495                 cfg.cn = [
13496                     
13497                     {
13498                         tag: 'label',
13499                         'for' :  id,
13500                         cls : 'control-label col-md-' + this.labelWidth,
13501                         html : this.fieldLabel
13502                         
13503                     },
13504                     {
13505                         cls : "col-md-" + (12 - this.labelWidth), 
13506                         cn: [
13507                             inputblock
13508                         ]
13509                     }
13510                     
13511                 ];
13512         } else if ( this.fieldLabel.length) {
13513                 Roo.log(" label");
13514                 cfg.cn = [
13515                    
13516                     {
13517                         tag: this.boxLabel ? 'span' : 'label',
13518                         'for': id,
13519                         cls: 'control-label box-input-label',
13520                         //cls : 'input-group-addon',
13521                         html : this.fieldLabel
13522                         
13523                     },
13524                     
13525                     inputblock
13526                     
13527                 ];
13528
13529         } else {
13530             
13531                    Roo.log(" no label && no align");
13532                 cfg.cn = [
13533                     
13534                         inputblock
13535                     
13536                 ];
13537                 
13538                 
13539         };
13540         
13541         if(this.boxLabel){
13542             cfg.cn.push({
13543                 tag: 'label',
13544                 'for': id,
13545                 cls: 'box-label',
13546                 html: this.boxLabel
13547             })
13548         }
13549         
13550         return cfg;
13551         
13552     },
13553     
13554     /**
13555      * return the real input element.
13556      */
13557     inputEl: function ()
13558     {
13559         return this.el.select('input.form-box',true).first();
13560     },
13561     
13562     label: function()
13563     {
13564         return this.el.select('label.control-label',true).first();
13565     },
13566     
13567     initEvents : function()
13568     {
13569 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13570         
13571         this.inputEl().on('click', this.onClick,  this);
13572         
13573     },
13574     
13575     onClick : function()
13576     {   
13577         this.setChecked(!this.checked);
13578     },
13579     
13580     setChecked : function(state,suppressEvent)
13581     {
13582         this.checked = state;
13583         
13584         this.inputEl().dom.checked = state;
13585         
13586         if(suppressEvent !== true){
13587             this.fireEvent('check', this, state);
13588         }
13589         
13590         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13591         
13592     },
13593     
13594     setValue : function(v,suppressEvent)
13595     {
13596         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13597     }
13598     
13599 });
13600
13601  
13602 /*
13603  * - LGPL
13604  *
13605  * Radio
13606  * 
13607  */
13608
13609 /**
13610  * @class Roo.bootstrap.Radio
13611  * @extends Roo.bootstrap.CheckBox
13612  * Bootstrap Radio class
13613
13614  * @constructor
13615  * Create a new Radio
13616  * @param {Object} config The config object
13617  */
13618
13619 Roo.bootstrap.Radio = function(config){
13620     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13621    
13622 };
13623
13624 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13625     
13626     inputType: 'radio',
13627     inputValue: '',
13628     valueOff: '',
13629     
13630     getAutoCreate : function()
13631     {
13632         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13633         
13634         var id = Roo.id();
13635         
13636         var cfg = {};
13637         
13638         cfg.cls = 'form-group' //input-group
13639         
13640         var input =  {
13641             tag: 'input',
13642             id : id,
13643             type : this.inputType,
13644             value : (!this.checked) ? this.valueOff : this.inputValue,
13645             cls : 'form-box',
13646             placeholder : this.placeholder || ''
13647             
13648         };
13649         
13650         if (this.disabled) {
13651             input.disabled=true;
13652         }
13653         
13654         if(this.checked){
13655             input.checked = this.checked;
13656         }
13657         
13658         if (this.name) {
13659             input.name = this.name;
13660         }
13661         
13662         if (this.size) {
13663             input.cls += ' input-' + this.size;
13664         }
13665         
13666         var settings=this;
13667         ['xs','sm','md','lg'].map(function(size){
13668             if (settings[size]) {
13669                 cfg.cls += ' col-' + size + '-' + settings[size];
13670             }
13671         });
13672         
13673         var inputblock = input;
13674         
13675         if (this.before || this.after) {
13676             
13677             inputblock = {
13678                 cls : 'input-group',
13679                 cn :  [] 
13680             };
13681             if (this.before) {
13682                 inputblock.cn.push({
13683                     tag :'span',
13684                     cls : 'input-group-addon',
13685                     html : this.before
13686                 });
13687             }
13688             inputblock.cn.push(input);
13689             if (this.after) {
13690                 inputblock.cn.push({
13691                     tag :'span',
13692                     cls : 'input-group-addon',
13693                     html : this.after
13694                 });
13695             }
13696             
13697         };
13698         
13699         if (align ==='left' && this.fieldLabel.length) {
13700                 Roo.log("left and has label");
13701                 cfg.cn = [
13702                     
13703                     {
13704                         tag: 'label',
13705                         'for' :  id,
13706                         cls : 'control-label col-md-' + this.labelWidth,
13707                         html : this.fieldLabel
13708                         
13709                     },
13710                     {
13711                         cls : "col-md-" + (12 - this.labelWidth), 
13712                         cn: [
13713                             inputblock
13714                         ]
13715                     }
13716                     
13717                 ];
13718         } else if ( this.fieldLabel.length) {
13719                 Roo.log(" label");
13720                  cfg.cn = [
13721                    
13722                     {
13723                         tag: 'label',
13724                         'for': id,
13725                         cls: 'control-label box-input-label',
13726                         //cls : 'input-group-addon',
13727                         html : this.fieldLabel
13728                         
13729                     },
13730                     
13731                     inputblock
13732                     
13733                 ];
13734
13735         } else {
13736             
13737                    Roo.log(" no label && no align");
13738                 cfg.cn = [
13739                     
13740                         inputblock
13741                     
13742                 ];
13743                 
13744                 
13745         };
13746         
13747         if(this.boxLabel){
13748             cfg.cn.push({
13749                 tag: 'label',
13750                 'for': id,
13751                 cls: 'box-label',
13752                 html: this.boxLabel
13753             })
13754         }
13755         
13756         return cfg;
13757         
13758     },
13759    
13760     onClick : function()
13761     {   
13762         this.setChecked(true);
13763     },
13764     
13765     setChecked : function(state,suppressEvent)
13766     {
13767         if(state){
13768             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13769                 v.dom.checked = false;
13770             });
13771         }
13772         
13773         this.checked = state;
13774         this.inputEl().dom.checked = state;
13775         
13776         if(suppressEvent !== true){
13777             this.fireEvent('check', this, state);
13778         }
13779         
13780         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13781         
13782     },
13783     
13784     getGroupValue : function()
13785     {
13786         var value = ''
13787         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13788             if(v.dom.checked == true){
13789                 value = v.dom.value;
13790             }
13791         });
13792         
13793         return value;
13794     },
13795     
13796     /**
13797      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13798      * @return {Mixed} value The field value
13799      */
13800     getValue : function(){
13801         return this.getGroupValue();
13802     }
13803     
13804 });
13805
13806  
13807 //<script type="text/javascript">
13808
13809 /*
13810  * Based  Ext JS Library 1.1.1
13811  * Copyright(c) 2006-2007, Ext JS, LLC.
13812  * LGPL
13813  *
13814  */
13815  
13816 /**
13817  * @class Roo.HtmlEditorCore
13818  * @extends Roo.Component
13819  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13820  *
13821  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13822  */
13823
13824 Roo.HtmlEditorCore = function(config){
13825     
13826     
13827     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13828     this.addEvents({
13829         /**
13830          * @event initialize
13831          * Fires when the editor is fully initialized (including the iframe)
13832          * @param {Roo.HtmlEditorCore} this
13833          */
13834         initialize: true,
13835         /**
13836          * @event activate
13837          * Fires when the editor is first receives the focus. Any insertion must wait
13838          * until after this event.
13839          * @param {Roo.HtmlEditorCore} this
13840          */
13841         activate: true,
13842          /**
13843          * @event beforesync
13844          * Fires before the textarea is updated with content from the editor iframe. Return false
13845          * to cancel the sync.
13846          * @param {Roo.HtmlEditorCore} this
13847          * @param {String} html
13848          */
13849         beforesync: true,
13850          /**
13851          * @event beforepush
13852          * Fires before the iframe editor is updated with content from the textarea. Return false
13853          * to cancel the push.
13854          * @param {Roo.HtmlEditorCore} this
13855          * @param {String} html
13856          */
13857         beforepush: true,
13858          /**
13859          * @event sync
13860          * Fires when the textarea is updated with content from the editor iframe.
13861          * @param {Roo.HtmlEditorCore} this
13862          * @param {String} html
13863          */
13864         sync: true,
13865          /**
13866          * @event push
13867          * Fires when the iframe editor is updated with content from the textarea.
13868          * @param {Roo.HtmlEditorCore} this
13869          * @param {String} html
13870          */
13871         push: true,
13872         
13873         /**
13874          * @event editorevent
13875          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13876          * @param {Roo.HtmlEditorCore} this
13877          */
13878         editorevent: true
13879     });
13880      
13881 };
13882
13883
13884 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
13885
13886
13887      /**
13888      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
13889      */
13890     
13891     owner : false,
13892     
13893      /**
13894      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13895      *                        Roo.resizable.
13896      */
13897     resizable : false,
13898      /**
13899      * @cfg {Number} height (in pixels)
13900      */   
13901     height: 300,
13902    /**
13903      * @cfg {Number} width (in pixels)
13904      */   
13905     width: 500,
13906     
13907     /**
13908      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13909      * 
13910      */
13911     stylesheets: false,
13912     
13913     // id of frame..
13914     frameId: false,
13915     
13916     // private properties
13917     validationEvent : false,
13918     deferHeight: true,
13919     initialized : false,
13920     activated : false,
13921     sourceEditMode : false,
13922     onFocus : Roo.emptyFn,
13923     iframePad:3,
13924     hideMode:'offsets',
13925     
13926     clearUp: true,
13927     
13928      
13929     
13930
13931     /**
13932      * Protected method that will not generally be called directly. It
13933      * is called when the editor initializes the iframe with HTML contents. Override this method if you
13934      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13935      */
13936     getDocMarkup : function(){
13937         // body styles..
13938         var st = '';
13939         Roo.log(this.stylesheets);
13940         
13941         // inherit styels from page...?? 
13942         if (this.stylesheets === false) {
13943             
13944             Roo.get(document.head).select('style').each(function(node) {
13945                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13946             });
13947             
13948             Roo.get(document.head).select('link').each(function(node) { 
13949                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13950             });
13951             
13952         } else if (!this.stylesheets.length) {
13953                 // simple..
13954                 st = '<style type="text/css">' +
13955                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13956                    '</style>';
13957         } else {
13958             Roo.each(this.stylesheets, function(s) {
13959                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13960             });
13961             
13962         }
13963         
13964         st +=  '<style type="text/css">' +
13965             'IMG { cursor: pointer } ' +
13966         '</style>';
13967
13968         
13969         return '<html><head>' + st  +
13970             //<style type="text/css">' +
13971             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13972             //'</style>' +
13973             ' </head><body class="roo-htmleditor-body"></body></html>';
13974     },
13975
13976     // private
13977     onRender : function(ct, position)
13978     {
13979         var _t = this;
13980         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13981         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13982         
13983         
13984         this.el.dom.style.border = '0 none';
13985         this.el.dom.setAttribute('tabIndex', -1);
13986         this.el.addClass('x-hidden hide');
13987         
13988         
13989         
13990         if(Roo.isIE){ // fix IE 1px bogus margin
13991             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13992         }
13993        
13994         
13995         this.frameId = Roo.id();
13996         
13997          
13998         
13999         var iframe = this.owner.wrap.createChild({
14000             tag: 'iframe',
14001             cls: 'form-control', // bootstrap..
14002             id: this.frameId,
14003             name: this.frameId,
14004             frameBorder : 'no',
14005             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14006         }, this.el
14007         );
14008         
14009         
14010         this.iframe = iframe.dom;
14011
14012          this.assignDocWin();
14013         
14014         this.doc.designMode = 'on';
14015        
14016         this.doc.open();
14017         this.doc.write(this.getDocMarkup());
14018         this.doc.close();
14019
14020         
14021         var task = { // must defer to wait for browser to be ready
14022             run : function(){
14023                 //console.log("run task?" + this.doc.readyState);
14024                 this.assignDocWin();
14025                 if(this.doc.body || this.doc.readyState == 'complete'){
14026                     try {
14027                         this.doc.designMode="on";
14028                     } catch (e) {
14029                         return;
14030                     }
14031                     Roo.TaskMgr.stop(task);
14032                     this.initEditor.defer(10, this);
14033                 }
14034             },
14035             interval : 10,
14036             duration: 10000,
14037             scope: this
14038         };
14039         Roo.TaskMgr.start(task);
14040
14041         
14042          
14043     },
14044
14045     // private
14046     onResize : function(w, h)
14047     {
14048          Roo.log('resize: ' +w + ',' + h );
14049         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14050         if(!this.iframe){
14051             return;
14052         }
14053         if(typeof w == 'number'){
14054             
14055             this.iframe.style.width = w + 'px';
14056         }
14057         if(typeof h == 'number'){
14058             
14059             this.iframe.style.height = h + 'px';
14060             if(this.doc){
14061                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14062             }
14063         }
14064         
14065     },
14066
14067     /**
14068      * Toggles the editor between standard and source edit mode.
14069      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14070      */
14071     toggleSourceEdit : function(sourceEditMode){
14072         
14073         this.sourceEditMode = sourceEditMode === true;
14074         
14075         if(this.sourceEditMode){
14076  
14077             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14078             
14079         }else{
14080             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14081             //this.iframe.className = '';
14082             this.deferFocus();
14083         }
14084         //this.setSize(this.owner.wrap.getSize());
14085         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14086     },
14087
14088     
14089   
14090
14091     /**
14092      * Protected method that will not generally be called directly. If you need/want
14093      * custom HTML cleanup, this is the method you should override.
14094      * @param {String} html The HTML to be cleaned
14095      * return {String} The cleaned HTML
14096      */
14097     cleanHtml : function(html){
14098         html = String(html);
14099         if(html.length > 5){
14100             if(Roo.isSafari){ // strip safari nonsense
14101                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14102             }
14103         }
14104         if(html == '&nbsp;'){
14105             html = '';
14106         }
14107         return html;
14108     },
14109
14110     /**
14111      * HTML Editor -> Textarea
14112      * Protected method that will not generally be called directly. Syncs the contents
14113      * of the editor iframe with the textarea.
14114      */
14115     syncValue : function(){
14116         if(this.initialized){
14117             var bd = (this.doc.body || this.doc.documentElement);
14118             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14119             var html = bd.innerHTML;
14120             if(Roo.isSafari){
14121                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14122                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14123                 if(m && m[1]){
14124                     html = '<div style="'+m[0]+'">' + html + '</div>';
14125                 }
14126             }
14127             html = this.cleanHtml(html);
14128             // fix up the special chars.. normaly like back quotes in word...
14129             // however we do not want to do this with chinese..
14130             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14131                 var cc = b.charCodeAt();
14132                 if (
14133                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14134                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14135                     (cc >= 0xf900 && cc < 0xfb00 )
14136                 ) {
14137                         return b;
14138                 }
14139                 return "&#"+cc+";" 
14140             });
14141             if(this.owner.fireEvent('beforesync', this, html) !== false){
14142                 this.el.dom.value = html;
14143                 this.owner.fireEvent('sync', this, html);
14144             }
14145         }
14146     },
14147
14148     /**
14149      * Protected method that will not generally be called directly. Pushes the value of the textarea
14150      * into the iframe editor.
14151      */
14152     pushValue : function(){
14153         if(this.initialized){
14154             var v = this.el.dom.value.trim();
14155             
14156 //            if(v.length < 1){
14157 //                v = '&#160;';
14158 //            }
14159             
14160             if(this.owner.fireEvent('beforepush', this, v) !== false){
14161                 var d = (this.doc.body || this.doc.documentElement);
14162                 d.innerHTML = v;
14163                 this.cleanUpPaste();
14164                 this.el.dom.value = d.innerHTML;
14165                 this.owner.fireEvent('push', this, v);
14166             }
14167         }
14168     },
14169
14170     // private
14171     deferFocus : function(){
14172         this.focus.defer(10, this);
14173     },
14174
14175     // doc'ed in Field
14176     focus : function(){
14177         if(this.win && !this.sourceEditMode){
14178             this.win.focus();
14179         }else{
14180             this.el.focus();
14181         }
14182     },
14183     
14184     assignDocWin: function()
14185     {
14186         var iframe = this.iframe;
14187         
14188          if(Roo.isIE){
14189             this.doc = iframe.contentWindow.document;
14190             this.win = iframe.contentWindow;
14191         } else {
14192             if (!Roo.get(this.frameId)) {
14193                 return;
14194             }
14195             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14196             this.win = Roo.get(this.frameId).dom.contentWindow;
14197         }
14198     },
14199     
14200     // private
14201     initEditor : function(){
14202         //console.log("INIT EDITOR");
14203         this.assignDocWin();
14204         
14205         
14206         
14207         this.doc.designMode="on";
14208         this.doc.open();
14209         this.doc.write(this.getDocMarkup());
14210         this.doc.close();
14211         
14212         var dbody = (this.doc.body || this.doc.documentElement);
14213         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14214         // this copies styles from the containing element into thsi one..
14215         // not sure why we need all of this..
14216         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14217         ss['background-attachment'] = 'fixed'; // w3c
14218         dbody.bgProperties = 'fixed'; // ie
14219         Roo.DomHelper.applyStyles(dbody, ss);
14220         Roo.EventManager.on(this.doc, {
14221             //'mousedown': this.onEditorEvent,
14222             'mouseup': this.onEditorEvent,
14223             'dblclick': this.onEditorEvent,
14224             'click': this.onEditorEvent,
14225             'keyup': this.onEditorEvent,
14226             buffer:100,
14227             scope: this
14228         });
14229         if(Roo.isGecko){
14230             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14231         }
14232         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14233             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14234         }
14235         this.initialized = true;
14236
14237         this.owner.fireEvent('initialize', this);
14238         this.pushValue();
14239     },
14240
14241     // private
14242     onDestroy : function(){
14243         
14244         
14245         
14246         if(this.rendered){
14247             
14248             //for (var i =0; i < this.toolbars.length;i++) {
14249             //    // fixme - ask toolbars for heights?
14250             //    this.toolbars[i].onDestroy();
14251            // }
14252             
14253             //this.wrap.dom.innerHTML = '';
14254             //this.wrap.remove();
14255         }
14256     },
14257
14258     // private
14259     onFirstFocus : function(){
14260         
14261         this.assignDocWin();
14262         
14263         
14264         this.activated = true;
14265          
14266     
14267         if(Roo.isGecko){ // prevent silly gecko errors
14268             this.win.focus();
14269             var s = this.win.getSelection();
14270             if(!s.focusNode || s.focusNode.nodeType != 3){
14271                 var r = s.getRangeAt(0);
14272                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14273                 r.collapse(true);
14274                 this.deferFocus();
14275             }
14276             try{
14277                 this.execCmd('useCSS', true);
14278                 this.execCmd('styleWithCSS', false);
14279             }catch(e){}
14280         }
14281         this.owner.fireEvent('activate', this);
14282     },
14283
14284     // private
14285     adjustFont: function(btn){
14286         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14287         //if(Roo.isSafari){ // safari
14288         //    adjust *= 2;
14289        // }
14290         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14291         if(Roo.isSafari){ // safari
14292             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14293             v =  (v < 10) ? 10 : v;
14294             v =  (v > 48) ? 48 : v;
14295             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14296             
14297         }
14298         
14299         
14300         v = Math.max(1, v+adjust);
14301         
14302         this.execCmd('FontSize', v  );
14303     },
14304
14305     onEditorEvent : function(e){
14306         this.owner.fireEvent('editorevent', this, e);
14307       //  this.updateToolbar();
14308         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14309     },
14310
14311     insertTag : function(tg)
14312     {
14313         // could be a bit smarter... -> wrap the current selected tRoo..
14314         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14315             
14316             range = this.createRange(this.getSelection());
14317             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14318             wrappingNode.appendChild(range.extractContents());
14319             range.insertNode(wrappingNode);
14320
14321             return;
14322             
14323             
14324             
14325         }
14326         this.execCmd("formatblock",   tg);
14327         
14328     },
14329     
14330     insertText : function(txt)
14331     {
14332         
14333         
14334         var range = this.createRange();
14335         range.deleteContents();
14336                //alert(Sender.getAttribute('label'));
14337                
14338         range.insertNode(this.doc.createTextNode(txt));
14339     } ,
14340     
14341      
14342
14343     /**
14344      * Executes a Midas editor command on the editor document and performs necessary focus and
14345      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14346      * @param {String} cmd The Midas command
14347      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14348      */
14349     relayCmd : function(cmd, value){
14350         this.win.focus();
14351         this.execCmd(cmd, value);
14352         this.owner.fireEvent('editorevent', this);
14353         //this.updateToolbar();
14354         this.owner.deferFocus();
14355     },
14356
14357     /**
14358      * Executes a Midas editor command directly on the editor document.
14359      * For visual commands, you should use {@link #relayCmd} instead.
14360      * <b>This should only be called after the editor is initialized.</b>
14361      * @param {String} cmd The Midas command
14362      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14363      */
14364     execCmd : function(cmd, value){
14365         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14366         this.syncValue();
14367     },
14368  
14369  
14370    
14371     /**
14372      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14373      * to insert tRoo.
14374      * @param {String} text | dom node.. 
14375      */
14376     insertAtCursor : function(text)
14377     {
14378         
14379         
14380         
14381         if(!this.activated){
14382             return;
14383         }
14384         /*
14385         if(Roo.isIE){
14386             this.win.focus();
14387             var r = this.doc.selection.createRange();
14388             if(r){
14389                 r.collapse(true);
14390                 r.pasteHTML(text);
14391                 this.syncValue();
14392                 this.deferFocus();
14393             
14394             }
14395             return;
14396         }
14397         */
14398         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14399             this.win.focus();
14400             
14401             
14402             // from jquery ui (MIT licenced)
14403             var range, node;
14404             var win = this.win;
14405             
14406             if (win.getSelection && win.getSelection().getRangeAt) {
14407                 range = win.getSelection().getRangeAt(0);
14408                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14409                 range.insertNode(node);
14410             } else if (win.document.selection && win.document.selection.createRange) {
14411                 // no firefox support
14412                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14413                 win.document.selection.createRange().pasteHTML(txt);
14414             } else {
14415                 // no firefox support
14416                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14417                 this.execCmd('InsertHTML', txt);
14418             } 
14419             
14420             this.syncValue();
14421             
14422             this.deferFocus();
14423         }
14424     },
14425  // private
14426     mozKeyPress : function(e){
14427         if(e.ctrlKey){
14428             var c = e.getCharCode(), cmd;
14429           
14430             if(c > 0){
14431                 c = String.fromCharCode(c).toLowerCase();
14432                 switch(c){
14433                     case 'b':
14434                         cmd = 'bold';
14435                         break;
14436                     case 'i':
14437                         cmd = 'italic';
14438                         break;
14439                     
14440                     case 'u':
14441                         cmd = 'underline';
14442                         break;
14443                     
14444                     case 'v':
14445                         this.cleanUpPaste.defer(100, this);
14446                         return;
14447                         
14448                 }
14449                 if(cmd){
14450                     this.win.focus();
14451                     this.execCmd(cmd);
14452                     this.deferFocus();
14453                     e.preventDefault();
14454                 }
14455                 
14456             }
14457         }
14458     },
14459
14460     // private
14461     fixKeys : function(){ // load time branching for fastest keydown performance
14462         if(Roo.isIE){
14463             return function(e){
14464                 var k = e.getKey(), r;
14465                 if(k == e.TAB){
14466                     e.stopEvent();
14467                     r = this.doc.selection.createRange();
14468                     if(r){
14469                         r.collapse(true);
14470                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14471                         this.deferFocus();
14472                     }
14473                     return;
14474                 }
14475                 
14476                 if(k == e.ENTER){
14477                     r = this.doc.selection.createRange();
14478                     if(r){
14479                         var target = r.parentElement();
14480                         if(!target || target.tagName.toLowerCase() != 'li'){
14481                             e.stopEvent();
14482                             r.pasteHTML('<br />');
14483                             r.collapse(false);
14484                             r.select();
14485                         }
14486                     }
14487                 }
14488                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14489                     this.cleanUpPaste.defer(100, this);
14490                     return;
14491                 }
14492                 
14493                 
14494             };
14495         }else if(Roo.isOpera){
14496             return function(e){
14497                 var k = e.getKey();
14498                 if(k == e.TAB){
14499                     e.stopEvent();
14500                     this.win.focus();
14501                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14502                     this.deferFocus();
14503                 }
14504                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14505                     this.cleanUpPaste.defer(100, this);
14506                     return;
14507                 }
14508                 
14509             };
14510         }else if(Roo.isSafari){
14511             return function(e){
14512                 var k = e.getKey();
14513                 
14514                 if(k == e.TAB){
14515                     e.stopEvent();
14516                     this.execCmd('InsertText','\t');
14517                     this.deferFocus();
14518                     return;
14519                 }
14520                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14521                     this.cleanUpPaste.defer(100, this);
14522                     return;
14523                 }
14524                 
14525              };
14526         }
14527     }(),
14528     
14529     getAllAncestors: function()
14530     {
14531         var p = this.getSelectedNode();
14532         var a = [];
14533         if (!p) {
14534             a.push(p); // push blank onto stack..
14535             p = this.getParentElement();
14536         }
14537         
14538         
14539         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14540             a.push(p);
14541             p = p.parentNode;
14542         }
14543         a.push(this.doc.body);
14544         return a;
14545     },
14546     lastSel : false,
14547     lastSelNode : false,
14548     
14549     
14550     getSelection : function() 
14551     {
14552         this.assignDocWin();
14553         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14554     },
14555     
14556     getSelectedNode: function() 
14557     {
14558         // this may only work on Gecko!!!
14559         
14560         // should we cache this!!!!
14561         
14562         
14563         
14564          
14565         var range = this.createRange(this.getSelection()).cloneRange();
14566         
14567         if (Roo.isIE) {
14568             var parent = range.parentElement();
14569             while (true) {
14570                 var testRange = range.duplicate();
14571                 testRange.moveToElementText(parent);
14572                 if (testRange.inRange(range)) {
14573                     break;
14574                 }
14575                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14576                     break;
14577                 }
14578                 parent = parent.parentElement;
14579             }
14580             return parent;
14581         }
14582         
14583         // is ancestor a text element.
14584         var ac =  range.commonAncestorContainer;
14585         if (ac.nodeType == 3) {
14586             ac = ac.parentNode;
14587         }
14588         
14589         var ar = ac.childNodes;
14590          
14591         var nodes = [];
14592         var other_nodes = [];
14593         var has_other_nodes = false;
14594         for (var i=0;i<ar.length;i++) {
14595             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14596                 continue;
14597             }
14598             // fullly contained node.
14599             
14600             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14601                 nodes.push(ar[i]);
14602                 continue;
14603             }
14604             
14605             // probably selected..
14606             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14607                 other_nodes.push(ar[i]);
14608                 continue;
14609             }
14610             // outer..
14611             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14612                 continue;
14613             }
14614             
14615             
14616             has_other_nodes = true;
14617         }
14618         if (!nodes.length && other_nodes.length) {
14619             nodes= other_nodes;
14620         }
14621         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14622             return false;
14623         }
14624         
14625         return nodes[0];
14626     },
14627     createRange: function(sel)
14628     {
14629         // this has strange effects when using with 
14630         // top toolbar - not sure if it's a great idea.
14631         //this.editor.contentWindow.focus();
14632         if (typeof sel != "undefined") {
14633             try {
14634                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14635             } catch(e) {
14636                 return this.doc.createRange();
14637             }
14638         } else {
14639             return this.doc.createRange();
14640         }
14641     },
14642     getParentElement: function()
14643     {
14644         
14645         this.assignDocWin();
14646         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14647         
14648         var range = this.createRange(sel);
14649          
14650         try {
14651             var p = range.commonAncestorContainer;
14652             while (p.nodeType == 3) { // text node
14653                 p = p.parentNode;
14654             }
14655             return p;
14656         } catch (e) {
14657             return null;
14658         }
14659     
14660     },
14661     /***
14662      *
14663      * Range intersection.. the hard stuff...
14664      *  '-1' = before
14665      *  '0' = hits..
14666      *  '1' = after.
14667      *         [ -- selected range --- ]
14668      *   [fail]                        [fail]
14669      *
14670      *    basically..
14671      *      if end is before start or  hits it. fail.
14672      *      if start is after end or hits it fail.
14673      *
14674      *   if either hits (but other is outside. - then it's not 
14675      *   
14676      *    
14677      **/
14678     
14679     
14680     // @see http://www.thismuchiknow.co.uk/?p=64.
14681     rangeIntersectsNode : function(range, node)
14682     {
14683         var nodeRange = node.ownerDocument.createRange();
14684         try {
14685             nodeRange.selectNode(node);
14686         } catch (e) {
14687             nodeRange.selectNodeContents(node);
14688         }
14689     
14690         var rangeStartRange = range.cloneRange();
14691         rangeStartRange.collapse(true);
14692     
14693         var rangeEndRange = range.cloneRange();
14694         rangeEndRange.collapse(false);
14695     
14696         var nodeStartRange = nodeRange.cloneRange();
14697         nodeStartRange.collapse(true);
14698     
14699         var nodeEndRange = nodeRange.cloneRange();
14700         nodeEndRange.collapse(false);
14701     
14702         return rangeStartRange.compareBoundaryPoints(
14703                  Range.START_TO_START, nodeEndRange) == -1 &&
14704                rangeEndRange.compareBoundaryPoints(
14705                  Range.START_TO_START, nodeStartRange) == 1;
14706         
14707          
14708     },
14709     rangeCompareNode : function(range, node)
14710     {
14711         var nodeRange = node.ownerDocument.createRange();
14712         try {
14713             nodeRange.selectNode(node);
14714         } catch (e) {
14715             nodeRange.selectNodeContents(node);
14716         }
14717         
14718         
14719         range.collapse(true);
14720     
14721         nodeRange.collapse(true);
14722      
14723         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14724         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
14725          
14726         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14727         
14728         var nodeIsBefore   =  ss == 1;
14729         var nodeIsAfter    = ee == -1;
14730         
14731         if (nodeIsBefore && nodeIsAfter)
14732             return 0; // outer
14733         if (!nodeIsBefore && nodeIsAfter)
14734             return 1; //right trailed.
14735         
14736         if (nodeIsBefore && !nodeIsAfter)
14737             return 2;  // left trailed.
14738         // fully contined.
14739         return 3;
14740     },
14741
14742     // private? - in a new class?
14743     cleanUpPaste :  function()
14744     {
14745         // cleans up the whole document..
14746         Roo.log('cleanuppaste');
14747         
14748         this.cleanUpChildren(this.doc.body);
14749         var clean = this.cleanWordChars(this.doc.body.innerHTML);
14750         if (clean != this.doc.body.innerHTML) {
14751             this.doc.body.innerHTML = clean;
14752         }
14753         
14754     },
14755     
14756     cleanWordChars : function(input) {// change the chars to hex code
14757         var he = Roo.HtmlEditorCore;
14758         
14759         var output = input;
14760         Roo.each(he.swapCodes, function(sw) { 
14761             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14762             
14763             output = output.replace(swapper, sw[1]);
14764         });
14765         
14766         return output;
14767     },
14768     
14769     
14770     cleanUpChildren : function (n)
14771     {
14772         if (!n.childNodes.length) {
14773             return;
14774         }
14775         for (var i = n.childNodes.length-1; i > -1 ; i--) {
14776            this.cleanUpChild(n.childNodes[i]);
14777         }
14778     },
14779     
14780     
14781         
14782     
14783     cleanUpChild : function (node)
14784     {
14785         var ed = this;
14786         //console.log(node);
14787         if (node.nodeName == "#text") {
14788             // clean up silly Windows -- stuff?
14789             return; 
14790         }
14791         if (node.nodeName == "#comment") {
14792             node.parentNode.removeChild(node);
14793             // clean up silly Windows -- stuff?
14794             return; 
14795         }
14796         
14797         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14798             // remove node.
14799             node.parentNode.removeChild(node);
14800             return;
14801             
14802         }
14803         
14804         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14805         
14806         // remove <a name=....> as rendering on yahoo mailer is borked with this.
14807         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14808         
14809         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14810         //    remove_keep_children = true;
14811         //}
14812         
14813         if (remove_keep_children) {
14814             this.cleanUpChildren(node);
14815             // inserts everything just before this node...
14816             while (node.childNodes.length) {
14817                 var cn = node.childNodes[0];
14818                 node.removeChild(cn);
14819                 node.parentNode.insertBefore(cn, node);
14820             }
14821             node.parentNode.removeChild(node);
14822             return;
14823         }
14824         
14825         if (!node.attributes || !node.attributes.length) {
14826             this.cleanUpChildren(node);
14827             return;
14828         }
14829         
14830         function cleanAttr(n,v)
14831         {
14832             
14833             if (v.match(/^\./) || v.match(/^\//)) {
14834                 return;
14835             }
14836             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14837                 return;
14838             }
14839             if (v.match(/^#/)) {
14840                 return;
14841             }
14842 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14843             node.removeAttribute(n);
14844             
14845         }
14846         
14847         function cleanStyle(n,v)
14848         {
14849             if (v.match(/expression/)) { //XSS?? should we even bother..
14850                 node.removeAttribute(n);
14851                 return;
14852             }
14853             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14854             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14855             
14856             
14857             var parts = v.split(/;/);
14858             var clean = [];
14859             
14860             Roo.each(parts, function(p) {
14861                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14862                 if (!p.length) {
14863                     return true;
14864                 }
14865                 var l = p.split(':').shift().replace(/\s+/g,'');
14866                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14867                 
14868                 if ( cblack.indexOf(l) > -1) {
14869 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14870                     //node.removeAttribute(n);
14871                     return true;
14872                 }
14873                 //Roo.log()
14874                 // only allow 'c whitelisted system attributes'
14875                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
14876 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14877                     //node.removeAttribute(n);
14878                     return true;
14879                 }
14880                 
14881                 
14882                  
14883                 
14884                 clean.push(p);
14885                 return true;
14886             });
14887             if (clean.length) { 
14888                 node.setAttribute(n, clean.join(';'));
14889             } else {
14890                 node.removeAttribute(n);
14891             }
14892             
14893         }
14894         
14895         
14896         for (var i = node.attributes.length-1; i > -1 ; i--) {
14897             var a = node.attributes[i];
14898             //console.log(a);
14899             
14900             if (a.name.toLowerCase().substr(0,2)=='on')  {
14901                 node.removeAttribute(a.name);
14902                 continue;
14903             }
14904             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14905                 node.removeAttribute(a.name);
14906                 continue;
14907             }
14908             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14909                 cleanAttr(a.name,a.value); // fixme..
14910                 continue;
14911             }
14912             if (a.name == 'style') {
14913                 cleanStyle(a.name,a.value);
14914                 continue;
14915             }
14916             /// clean up MS crap..
14917             // tecnically this should be a list of valid class'es..
14918             
14919             
14920             if (a.name == 'class') {
14921                 if (a.value.match(/^Mso/)) {
14922                     node.className = '';
14923                 }
14924                 
14925                 if (a.value.match(/body/)) {
14926                     node.className = '';
14927                 }
14928                 continue;
14929             }
14930             
14931             // style cleanup!?
14932             // class cleanup?
14933             
14934         }
14935         
14936         
14937         this.cleanUpChildren(node);
14938         
14939         
14940     }
14941     
14942     
14943     // hide stuff that is not compatible
14944     /**
14945      * @event blur
14946      * @hide
14947      */
14948     /**
14949      * @event change
14950      * @hide
14951      */
14952     /**
14953      * @event focus
14954      * @hide
14955      */
14956     /**
14957      * @event specialkey
14958      * @hide
14959      */
14960     /**
14961      * @cfg {String} fieldClass @hide
14962      */
14963     /**
14964      * @cfg {String} focusClass @hide
14965      */
14966     /**
14967      * @cfg {String} autoCreate @hide
14968      */
14969     /**
14970      * @cfg {String} inputType @hide
14971      */
14972     /**
14973      * @cfg {String} invalidClass @hide
14974      */
14975     /**
14976      * @cfg {String} invalidText @hide
14977      */
14978     /**
14979      * @cfg {String} msgFx @hide
14980      */
14981     /**
14982      * @cfg {String} validateOnBlur @hide
14983      */
14984 });
14985
14986 Roo.HtmlEditorCore.white = [
14987         'area', 'br', 'img', 'input', 'hr', 'wbr',
14988         
14989        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14990        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14991        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14992        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14993        'table',   'ul',         'xmp', 
14994        
14995        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14996       'thead',   'tr', 
14997      
14998       'dir', 'menu', 'ol', 'ul', 'dl',
14999        
15000       'embed',  'object'
15001 ];
15002
15003
15004 Roo.HtmlEditorCore.black = [
15005     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15006         'applet', // 
15007         'base',   'basefont', 'bgsound', 'blink',  'body', 
15008         'frame',  'frameset', 'head',    'html',   'ilayer', 
15009         'iframe', 'layer',  'link',     'meta',    'object',   
15010         'script', 'style' ,'title',  'xml' // clean later..
15011 ];
15012 Roo.HtmlEditorCore.clean = [
15013     'script', 'style', 'title', 'xml'
15014 ];
15015 Roo.HtmlEditorCore.remove = [
15016     'font'
15017 ];
15018 // attributes..
15019
15020 Roo.HtmlEditorCore.ablack = [
15021     'on'
15022 ];
15023     
15024 Roo.HtmlEditorCore.aclean = [ 
15025     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15026 ];
15027
15028 // protocols..
15029 Roo.HtmlEditorCore.pwhite= [
15030         'http',  'https',  'mailto'
15031 ];
15032
15033 // white listed style attributes.
15034 Roo.HtmlEditorCore.cwhite= [
15035       //  'text-align', /// default is to allow most things..
15036       
15037          
15038 //        'font-size'//??
15039 ];
15040
15041 // black listed style attributes.
15042 Roo.HtmlEditorCore.cblack= [
15043       //  'font-size' -- this can be set by the project 
15044 ];
15045
15046
15047 Roo.HtmlEditorCore.swapCodes   =[ 
15048     [    8211, "--" ], 
15049     [    8212, "--" ], 
15050     [    8216,  "'" ],  
15051     [    8217, "'" ],  
15052     [    8220, '"' ],  
15053     [    8221, '"' ],  
15054     [    8226, "*" ],  
15055     [    8230, "..." ]
15056 ]; 
15057
15058     /*
15059  * - LGPL
15060  *
15061  * HtmlEditor
15062  * 
15063  */
15064
15065 /**
15066  * @class Roo.bootstrap.HtmlEditor
15067  * @extends Roo.bootstrap.TextArea
15068  * Bootstrap HtmlEditor class
15069
15070  * @constructor
15071  * Create a new HtmlEditor
15072  * @param {Object} config The config object
15073  */
15074
15075 Roo.bootstrap.HtmlEditor = function(config){
15076     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15077     if (!this.toolbars) {
15078         this.toolbars = [];
15079     }
15080     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15081     this.addEvents({
15082             /**
15083              * @event initialize
15084              * Fires when the editor is fully initialized (including the iframe)
15085              * @param {HtmlEditor} this
15086              */
15087             initialize: true,
15088             /**
15089              * @event activate
15090              * Fires when the editor is first receives the focus. Any insertion must wait
15091              * until after this event.
15092              * @param {HtmlEditor} this
15093              */
15094             activate: true,
15095              /**
15096              * @event beforesync
15097              * Fires before the textarea is updated with content from the editor iframe. Return false
15098              * to cancel the sync.
15099              * @param {HtmlEditor} this
15100              * @param {String} html
15101              */
15102             beforesync: true,
15103              /**
15104              * @event beforepush
15105              * Fires before the iframe editor is updated with content from the textarea. Return false
15106              * to cancel the push.
15107              * @param {HtmlEditor} this
15108              * @param {String} html
15109              */
15110             beforepush: true,
15111              /**
15112              * @event sync
15113              * Fires when the textarea is updated with content from the editor iframe.
15114              * @param {HtmlEditor} this
15115              * @param {String} html
15116              */
15117             sync: true,
15118              /**
15119              * @event push
15120              * Fires when the iframe editor is updated with content from the textarea.
15121              * @param {HtmlEditor} this
15122              * @param {String} html
15123              */
15124             push: true,
15125              /**
15126              * @event editmodechange
15127              * Fires when the editor switches edit modes
15128              * @param {HtmlEditor} this
15129              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15130              */
15131             editmodechange: true,
15132             /**
15133              * @event editorevent
15134              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15135              * @param {HtmlEditor} this
15136              */
15137             editorevent: true,
15138             /**
15139              * @event firstfocus
15140              * Fires when on first focus - needed by toolbars..
15141              * @param {HtmlEditor} this
15142              */
15143             firstfocus: true,
15144             /**
15145              * @event autosave
15146              * Auto save the htmlEditor value as a file into Events
15147              * @param {HtmlEditor} this
15148              */
15149             autosave: true,
15150             /**
15151              * @event savedpreview
15152              * preview the saved version of htmlEditor
15153              * @param {HtmlEditor} this
15154              */
15155             savedpreview: true
15156         });
15157 };
15158
15159
15160 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15161     
15162     
15163       /**
15164      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15165      */
15166     toolbars : false,
15167    
15168      /**
15169      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15170      *                        Roo.resizable.
15171      */
15172     resizable : false,
15173      /**
15174      * @cfg {Number} height (in pixels)
15175      */   
15176     height: 300,
15177    /**
15178      * @cfg {Number} width (in pixels)
15179      */   
15180     width: false,
15181     
15182     /**
15183      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15184      * 
15185      */
15186     stylesheets: false,
15187     
15188     // id of frame..
15189     frameId: false,
15190     
15191     // private properties
15192     validationEvent : false,
15193     deferHeight: true,
15194     initialized : false,
15195     activated : false,
15196     
15197     onFocus : Roo.emptyFn,
15198     iframePad:3,
15199     hideMode:'offsets',
15200     
15201     
15202     tbContainer : false,
15203     
15204     toolbarContainer :function() {
15205         return this.wrap.select('.x-html-editor-tb',true).first();
15206     },
15207
15208     /**
15209      * Protected method that will not generally be called directly. It
15210      * is called when the editor creates its toolbar. Override this method if you need to
15211      * add custom toolbar buttons.
15212      * @param {HtmlEditor} editor
15213      */
15214     createToolbar : function(){
15215         
15216         Roo.log("create toolbars");
15217         
15218         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15219         this.toolbars[0].render(this.toolbarContainer());
15220         
15221         return;
15222         
15223 //        if (!editor.toolbars || !editor.toolbars.length) {
15224 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15225 //        }
15226 //        
15227 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15228 //            editor.toolbars[i] = Roo.factory(
15229 //                    typeof(editor.toolbars[i]) == 'string' ?
15230 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15231 //                Roo.bootstrap.HtmlEditor);
15232 //            editor.toolbars[i].init(editor);
15233 //        }
15234     },
15235
15236      
15237     // private
15238     onRender : function(ct, position)
15239     {
15240        // Roo.log("Call onRender: " + this.xtype);
15241         var _t = this;
15242         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15243       
15244         this.wrap = this.inputEl().wrap({
15245             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15246         });
15247         
15248         this.editorcore.onRender(ct, position);
15249          
15250         if (this.resizable) {
15251             this.resizeEl = new Roo.Resizable(this.wrap, {
15252                 pinned : true,
15253                 wrap: true,
15254                 dynamic : true,
15255                 minHeight : this.height,
15256                 height: this.height,
15257                 handles : this.resizable,
15258                 width: this.width,
15259                 listeners : {
15260                     resize : function(r, w, h) {
15261                         _t.onResize(w,h); // -something
15262                     }
15263                 }
15264             });
15265             
15266         }
15267         this.createToolbar(this);
15268        
15269         
15270         if(!this.width && this.resizable){
15271             this.setSize(this.wrap.getSize());
15272         }
15273         if (this.resizeEl) {
15274             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15275             // should trigger onReize..
15276         }
15277         
15278     },
15279
15280     // private
15281     onResize : function(w, h)
15282     {
15283         Roo.log('resize: ' +w + ',' + h );
15284         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15285         var ew = false;
15286         var eh = false;
15287         
15288         if(this.inputEl() ){
15289             if(typeof w == 'number'){
15290                 var aw = w - this.wrap.getFrameWidth('lr');
15291                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15292                 ew = aw;
15293             }
15294             if(typeof h == 'number'){
15295                  var tbh = -11;  // fixme it needs to tool bar size!
15296                 for (var i =0; i < this.toolbars.length;i++) {
15297                     // fixme - ask toolbars for heights?
15298                     tbh += this.toolbars[i].el.getHeight();
15299                     //if (this.toolbars[i].footer) {
15300                     //    tbh += this.toolbars[i].footer.el.getHeight();
15301                     //}
15302                 }
15303               
15304                 
15305                 
15306                 
15307                 
15308                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15309                 ah -= 5; // knock a few pixes off for look..
15310                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15311                 var eh = ah;
15312             }
15313         }
15314         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15315         this.editorcore.onResize(ew,eh);
15316         
15317     },
15318
15319     /**
15320      * Toggles the editor between standard and source edit mode.
15321      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15322      */
15323     toggleSourceEdit : function(sourceEditMode)
15324     {
15325         this.editorcore.toggleSourceEdit(sourceEditMode);
15326         
15327         if(this.editorcore.sourceEditMode){
15328             Roo.log('editor - showing textarea');
15329             
15330 //            Roo.log('in');
15331 //            Roo.log(this.syncValue());
15332             this.syncValue();
15333             this.inputEl().removeClass('hide');
15334             this.inputEl().dom.removeAttribute('tabIndex');
15335             this.inputEl().focus();
15336         }else{
15337             Roo.log('editor - hiding textarea');
15338 //            Roo.log('out')
15339 //            Roo.log(this.pushValue()); 
15340             this.pushValue();
15341             
15342             this.inputEl().addClass('hide');
15343             this.inputEl().dom.setAttribute('tabIndex', -1);
15344             //this.deferFocus();
15345         }
15346          
15347         if(this.resizable){
15348             this.setSize(this.wrap.getSize());
15349         }
15350         
15351         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15352     },
15353  
15354     // private (for BoxComponent)
15355     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15356
15357     // private (for BoxComponent)
15358     getResizeEl : function(){
15359         return this.wrap;
15360     },
15361
15362     // private (for BoxComponent)
15363     getPositionEl : function(){
15364         return this.wrap;
15365     },
15366
15367     // private
15368     initEvents : function(){
15369         this.originalValue = this.getValue();
15370     },
15371
15372 //    /**
15373 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15374 //     * @method
15375 //     */
15376 //    markInvalid : Roo.emptyFn,
15377 //    /**
15378 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15379 //     * @method
15380 //     */
15381 //    clearInvalid : Roo.emptyFn,
15382
15383     setValue : function(v){
15384         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15385         this.editorcore.pushValue();
15386     },
15387
15388      
15389     // private
15390     deferFocus : function(){
15391         this.focus.defer(10, this);
15392     },
15393
15394     // doc'ed in Field
15395     focus : function(){
15396         this.editorcore.focus();
15397         
15398     },
15399       
15400
15401     // private
15402     onDestroy : function(){
15403         
15404         
15405         
15406         if(this.rendered){
15407             
15408             for (var i =0; i < this.toolbars.length;i++) {
15409                 // fixme - ask toolbars for heights?
15410                 this.toolbars[i].onDestroy();
15411             }
15412             
15413             this.wrap.dom.innerHTML = '';
15414             this.wrap.remove();
15415         }
15416     },
15417
15418     // private
15419     onFirstFocus : function(){
15420         //Roo.log("onFirstFocus");
15421         this.editorcore.onFirstFocus();
15422          for (var i =0; i < this.toolbars.length;i++) {
15423             this.toolbars[i].onFirstFocus();
15424         }
15425         
15426     },
15427     
15428     // private
15429     syncValue : function()
15430     {   
15431         this.editorcore.syncValue();
15432     },
15433     
15434     pushValue : function()
15435     {   
15436         this.editorcore.pushValue();
15437     }
15438      
15439     
15440     // hide stuff that is not compatible
15441     /**
15442      * @event blur
15443      * @hide
15444      */
15445     /**
15446      * @event change
15447      * @hide
15448      */
15449     /**
15450      * @event focus
15451      * @hide
15452      */
15453     /**
15454      * @event specialkey
15455      * @hide
15456      */
15457     /**
15458      * @cfg {String} fieldClass @hide
15459      */
15460     /**
15461      * @cfg {String} focusClass @hide
15462      */
15463     /**
15464      * @cfg {String} autoCreate @hide
15465      */
15466     /**
15467      * @cfg {String} inputType @hide
15468      */
15469     /**
15470      * @cfg {String} invalidClass @hide
15471      */
15472     /**
15473      * @cfg {String} invalidText @hide
15474      */
15475     /**
15476      * @cfg {String} msgFx @hide
15477      */
15478     /**
15479      * @cfg {String} validateOnBlur @hide
15480      */
15481 });
15482  
15483     
15484    
15485    
15486    
15487       
15488
15489 /**
15490  * @class Roo.bootstrap.HtmlEditorToolbar1
15491  * Basic Toolbar
15492  * 
15493  * Usage:
15494  *
15495  new Roo.bootstrap.HtmlEditor({
15496     ....
15497     toolbars : [
15498         new Roo.bootstrap.HtmlEditorToolbar1({
15499             disable : { fonts: 1 , format: 1, ..., ... , ...],
15500             btns : [ .... ]
15501         })
15502     }
15503      
15504  * 
15505  * @cfg {Object} disable List of elements to disable..
15506  * @cfg {Array} btns List of additional buttons.
15507  * 
15508  * 
15509  * NEEDS Extra CSS? 
15510  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15511  */
15512  
15513 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15514 {
15515     
15516     Roo.apply(this, config);
15517     
15518     // default disabled, based on 'good practice'..
15519     this.disable = this.disable || {};
15520     Roo.applyIf(this.disable, {
15521         fontSize : true,
15522         colors : true,
15523         specialElements : true
15524     });
15525     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15526     
15527     this.editor = config.editor;
15528     this.editorcore = config.editor.editorcore;
15529     
15530     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15531     
15532     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15533     // dont call parent... till later.
15534 }
15535 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
15536     
15537     
15538     bar : true,
15539     
15540     editor : false,
15541     editorcore : false,
15542     
15543     
15544     formats : [
15545         "p" ,  
15546         "h1","h2","h3","h4","h5","h6", 
15547         "pre", "code", 
15548         "abbr", "acronym", "address", "cite", "samp", "var",
15549         'div','span'
15550     ],
15551     
15552     onRender : function(ct, position)
15553     {
15554        // Roo.log("Call onRender: " + this.xtype);
15555         
15556        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15557        Roo.log(this.el);
15558        this.el.dom.style.marginBottom = '0';
15559        var _this = this;
15560        var editorcore = this.editorcore;
15561        var editor= this.editor;
15562        
15563        var children = [];
15564        var btn = function(id,cmd , toggle, handler){
15565        
15566             var  event = toggle ? 'toggle' : 'click';
15567        
15568             var a = {
15569                 size : 'sm',
15570                 xtype: 'Button',
15571                 xns: Roo.bootstrap,
15572                 glyphicon : id,
15573                 cmd : id || cmd,
15574                 enableToggle:toggle !== false,
15575                 //html : 'submit'
15576                 pressed : toggle ? false : null,
15577                 listeners : {}
15578             }
15579             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15580                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15581             }
15582             children.push(a);
15583             return a;
15584        }
15585         
15586         var style = {
15587                 xtype: 'Button',
15588                 size : 'sm',
15589                 xns: Roo.bootstrap,
15590                 glyphicon : 'font',
15591                 //html : 'submit'
15592                 menu : {
15593                     xtype: 'Menu',
15594                     xns: Roo.bootstrap,
15595                     items:  []
15596                 }
15597         };
15598         Roo.each(this.formats, function(f) {
15599             style.menu.items.push({
15600                 xtype :'MenuItem',
15601                 xns: Roo.bootstrap,
15602                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15603                 tagname : f,
15604                 listeners : {
15605                     click : function()
15606                     {
15607                         editorcore.insertTag(this.tagname);
15608                         editor.focus();
15609                     }
15610                 }
15611                 
15612             });
15613         });
15614          children.push(style);   
15615             
15616             
15617         btn('bold',false,true);
15618         btn('italic',false,true);
15619         btn('align-left', 'justifyleft',true);
15620         btn('align-center', 'justifycenter',true);
15621         btn('align-right' , 'justifyright',true);
15622         btn('link', false, false, function(btn) {
15623             //Roo.log("create link?");
15624             var url = prompt(this.createLinkText, this.defaultLinkValue);
15625             if(url && url != 'http:/'+'/'){
15626                 this.editorcore.relayCmd('createlink', url);
15627             }
15628         }),
15629         btn('list','insertunorderedlist',true);
15630         btn('pencil', false,true, function(btn){
15631                 Roo.log(this);
15632                 
15633                 this.toggleSourceEdit(btn.pressed);
15634         });
15635         /*
15636         var cog = {
15637                 xtype: 'Button',
15638                 size : 'sm',
15639                 xns: Roo.bootstrap,
15640                 glyphicon : 'cog',
15641                 //html : 'submit'
15642                 menu : {
15643                     xtype: 'Menu',
15644                     xns: Roo.bootstrap,
15645                     items:  []
15646                 }
15647         };
15648         
15649         cog.menu.items.push({
15650             xtype :'MenuItem',
15651             xns: Roo.bootstrap,
15652             html : Clean styles,
15653             tagname : f,
15654             listeners : {
15655                 click : function()
15656                 {
15657                     editorcore.insertTag(this.tagname);
15658                     editor.focus();
15659                 }
15660             }
15661             
15662         });
15663        */
15664         
15665          
15666        this.xtype = 'Navbar';
15667         
15668         for(var i=0;i< children.length;i++) {
15669             
15670             this.buttons.add(this.addxtypeChild(children[i]));
15671             
15672         }
15673         
15674         editor.on('editorevent', this.updateToolbar, this);
15675     },
15676     onBtnClick : function(id)
15677     {
15678        this.editorcore.relayCmd(id);
15679        this.editorcore.focus();
15680     },
15681     
15682     /**
15683      * Protected method that will not generally be called directly. It triggers
15684      * a toolbar update by reading the markup state of the current selection in the editor.
15685      */
15686     updateToolbar: function(){
15687
15688         if(!this.editorcore.activated){
15689             this.editor.onFirstFocus(); // is this neeed?
15690             return;
15691         }
15692
15693         var btns = this.buttons; 
15694         var doc = this.editorcore.doc;
15695         btns.get('bold').setActive(doc.queryCommandState('bold'));
15696         btns.get('italic').setActive(doc.queryCommandState('italic'));
15697         //btns.get('underline').setActive(doc.queryCommandState('underline'));
15698         
15699         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15700         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15701         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15702         
15703         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15704         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15705          /*
15706         
15707         var ans = this.editorcore.getAllAncestors();
15708         if (this.formatCombo) {
15709             
15710             
15711             var store = this.formatCombo.store;
15712             this.formatCombo.setValue("");
15713             for (var i =0; i < ans.length;i++) {
15714                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15715                     // select it..
15716                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15717                     break;
15718                 }
15719             }
15720         }
15721         
15722         
15723         
15724         // hides menus... - so this cant be on a menu...
15725         Roo.bootstrap.MenuMgr.hideAll();
15726         */
15727         Roo.bootstrap.MenuMgr.hideAll();
15728         //this.editorsyncValue();
15729     },
15730     onFirstFocus: function() {
15731         this.buttons.each(function(item){
15732            item.enable();
15733         });
15734     },
15735     toggleSourceEdit : function(sourceEditMode){
15736         
15737           
15738         if(sourceEditMode){
15739             Roo.log("disabling buttons");
15740            this.buttons.each( function(item){
15741                 if(item.cmd != 'pencil'){
15742                     item.disable();
15743                 }
15744             });
15745           
15746         }else{
15747             Roo.log("enabling buttons");
15748             if(this.editorcore.initialized){
15749                 this.buttons.each( function(item){
15750                     item.enable();
15751                 });
15752             }
15753             
15754         }
15755         Roo.log("calling toggole on editor");
15756         // tell the editor that it's been pressed..
15757         this.editor.toggleSourceEdit(sourceEditMode);
15758        
15759     }
15760 });
15761
15762
15763
15764
15765
15766 /**
15767  * @class Roo.bootstrap.Table.AbstractSelectionModel
15768  * @extends Roo.util.Observable
15769  * Abstract base class for grid SelectionModels.  It provides the interface that should be
15770  * implemented by descendant classes.  This class should not be directly instantiated.
15771  * @constructor
15772  */
15773 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15774     this.locked = false;
15775     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15776 };
15777
15778
15779 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
15780     /** @ignore Called by the grid automatically. Do not call directly. */
15781     init : function(grid){
15782         this.grid = grid;
15783         this.initEvents();
15784     },
15785
15786     /**
15787      * Locks the selections.
15788      */
15789     lock : function(){
15790         this.locked = true;
15791     },
15792
15793     /**
15794      * Unlocks the selections.
15795      */
15796     unlock : function(){
15797         this.locked = false;
15798     },
15799
15800     /**
15801      * Returns true if the selections are locked.
15802      * @return {Boolean}
15803      */
15804     isLocked : function(){
15805         return this.locked;
15806     }
15807 });
15808 /**
15809  * @class Roo.bootstrap.Table.ColumnModel
15810  * @extends Roo.util.Observable
15811  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15812  * the columns in the table.
15813  
15814  * @constructor
15815  * @param {Object} config An Array of column config objects. See this class's
15816  * config objects for details.
15817 */
15818 Roo.bootstrap.Table.ColumnModel = function(config){
15819         /**
15820      * The config passed into the constructor
15821      */
15822     this.config = config;
15823     this.lookup = {};
15824
15825     // if no id, create one
15826     // if the column does not have a dataIndex mapping,
15827     // map it to the order it is in the config
15828     for(var i = 0, len = config.length; i < len; i++){
15829         var c = config[i];
15830         if(typeof c.dataIndex == "undefined"){
15831             c.dataIndex = i;
15832         }
15833         if(typeof c.renderer == "string"){
15834             c.renderer = Roo.util.Format[c.renderer];
15835         }
15836         if(typeof c.id == "undefined"){
15837             c.id = Roo.id();
15838         }
15839 //        if(c.editor && c.editor.xtype){
15840 //            c.editor  = Roo.factory(c.editor, Roo.grid);
15841 //        }
15842 //        if(c.editor && c.editor.isFormField){
15843 //            c.editor = new Roo.grid.GridEditor(c.editor);
15844 //        }
15845
15846         this.lookup[c.id] = c;
15847     }
15848
15849     /**
15850      * The width of columns which have no width specified (defaults to 100)
15851      * @type Number
15852      */
15853     this.defaultWidth = 100;
15854
15855     /**
15856      * Default sortable of columns which have no sortable specified (defaults to false)
15857      * @type Boolean
15858      */
15859     this.defaultSortable = false;
15860
15861     this.addEvents({
15862         /**
15863              * @event widthchange
15864              * Fires when the width of a column changes.
15865              * @param {ColumnModel} this
15866              * @param {Number} columnIndex The column index
15867              * @param {Number} newWidth The new width
15868              */
15869             "widthchange": true,
15870         /**
15871              * @event headerchange
15872              * Fires when the text of a header changes.
15873              * @param {ColumnModel} this
15874              * @param {Number} columnIndex The column index
15875              * @param {Number} newText The new header text
15876              */
15877             "headerchange": true,
15878         /**
15879              * @event hiddenchange
15880              * Fires when a column is hidden or "unhidden".
15881              * @param {ColumnModel} this
15882              * @param {Number} columnIndex The column index
15883              * @param {Boolean} hidden true if hidden, false otherwise
15884              */
15885             "hiddenchange": true,
15886             /**
15887          * @event columnmoved
15888          * Fires when a column is moved.
15889          * @param {ColumnModel} this
15890          * @param {Number} oldIndex
15891          * @param {Number} newIndex
15892          */
15893         "columnmoved" : true,
15894         /**
15895          * @event columlockchange
15896          * Fires when a column's locked state is changed
15897          * @param {ColumnModel} this
15898          * @param {Number} colIndex
15899          * @param {Boolean} locked true if locked
15900          */
15901         "columnlockchange" : true
15902     });
15903     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15904 };
15905 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15906     /**
15907      * @cfg {String} header The header text to display in the Grid view.
15908      */
15909     /**
15910      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15911      * {@link Roo.data.Record} definition from which to draw the column's value. If not
15912      * specified, the column's index is used as an index into the Record's data Array.
15913      */
15914     /**
15915      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15916      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15917      */
15918     /**
15919      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15920      * Defaults to the value of the {@link #defaultSortable} property.
15921      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15922      */
15923     /**
15924      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
15925      */
15926     /**
15927      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
15928      */
15929     /**
15930      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15931      */
15932     /**
15933      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15934      */
15935     /**
15936      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15937      * given the cell's data value. See {@link #setRenderer}. If not specified, the
15938      * default renderer uses the raw data value.
15939      */
15940     /**
15941      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
15942      */
15943
15944     /**
15945      * Returns the id of the column at the specified index.
15946      * @param {Number} index The column index
15947      * @return {String} the id
15948      */
15949     getColumnId : function(index){
15950         return this.config[index].id;
15951     },
15952
15953     /**
15954      * Returns the column for a specified id.
15955      * @param {String} id The column id
15956      * @return {Object} the column
15957      */
15958     getColumnById : function(id){
15959         return this.lookup[id];
15960     },
15961
15962     
15963     /**
15964      * Returns the column for a specified dataIndex.
15965      * @param {String} dataIndex The column dataIndex
15966      * @return {Object|Boolean} the column or false if not found
15967      */
15968     getColumnByDataIndex: function(dataIndex){
15969         var index = this.findColumnIndex(dataIndex);
15970         return index > -1 ? this.config[index] : false;
15971     },
15972     
15973     /**
15974      * Returns the index for a specified column id.
15975      * @param {String} id The column id
15976      * @return {Number} the index, or -1 if not found
15977      */
15978     getIndexById : function(id){
15979         for(var i = 0, len = this.config.length; i < len; i++){
15980             if(this.config[i].id == id){
15981                 return i;
15982             }
15983         }
15984         return -1;
15985     },
15986     
15987     /**
15988      * Returns the index for a specified column dataIndex.
15989      * @param {String} dataIndex The column dataIndex
15990      * @return {Number} the index, or -1 if not found
15991      */
15992     
15993     findColumnIndex : function(dataIndex){
15994         for(var i = 0, len = this.config.length; i < len; i++){
15995             if(this.config[i].dataIndex == dataIndex){
15996                 return i;
15997             }
15998         }
15999         return -1;
16000     },
16001     
16002     
16003     moveColumn : function(oldIndex, newIndex){
16004         var c = this.config[oldIndex];
16005         this.config.splice(oldIndex, 1);
16006         this.config.splice(newIndex, 0, c);
16007         this.dataMap = null;
16008         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16009     },
16010
16011     isLocked : function(colIndex){
16012         return this.config[colIndex].locked === true;
16013     },
16014
16015     setLocked : function(colIndex, value, suppressEvent){
16016         if(this.isLocked(colIndex) == value){
16017             return;
16018         }
16019         this.config[colIndex].locked = value;
16020         if(!suppressEvent){
16021             this.fireEvent("columnlockchange", this, colIndex, value);
16022         }
16023     },
16024
16025     getTotalLockedWidth : function(){
16026         var totalWidth = 0;
16027         for(var i = 0; i < this.config.length; i++){
16028             if(this.isLocked(i) && !this.isHidden(i)){
16029                 this.totalWidth += this.getColumnWidth(i);
16030             }
16031         }
16032         return totalWidth;
16033     },
16034
16035     getLockedCount : function(){
16036         for(var i = 0, len = this.config.length; i < len; i++){
16037             if(!this.isLocked(i)){
16038                 return i;
16039             }
16040         }
16041     },
16042
16043     /**
16044      * Returns the number of columns.
16045      * @return {Number}
16046      */
16047     getColumnCount : function(visibleOnly){
16048         if(visibleOnly === true){
16049             var c = 0;
16050             for(var i = 0, len = this.config.length; i < len; i++){
16051                 if(!this.isHidden(i)){
16052                     c++;
16053                 }
16054             }
16055             return c;
16056         }
16057         return this.config.length;
16058     },
16059
16060     /**
16061      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16062      * @param {Function} fn
16063      * @param {Object} scope (optional)
16064      * @return {Array} result
16065      */
16066     getColumnsBy : function(fn, scope){
16067         var r = [];
16068         for(var i = 0, len = this.config.length; i < len; i++){
16069             var c = this.config[i];
16070             if(fn.call(scope||this, c, i) === true){
16071                 r[r.length] = c;
16072             }
16073         }
16074         return r;
16075     },
16076
16077     /**
16078      * Returns true if the specified column is sortable.
16079      * @param {Number} col The column index
16080      * @return {Boolean}
16081      */
16082     isSortable : function(col){
16083         if(typeof this.config[col].sortable == "undefined"){
16084             return this.defaultSortable;
16085         }
16086         return this.config[col].sortable;
16087     },
16088
16089     /**
16090      * Returns the rendering (formatting) function defined for the column.
16091      * @param {Number} col The column index.
16092      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16093      */
16094     getRenderer : function(col){
16095         if(!this.config[col].renderer){
16096             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16097         }
16098         return this.config[col].renderer;
16099     },
16100
16101     /**
16102      * Sets the rendering (formatting) function for a column.
16103      * @param {Number} col The column index
16104      * @param {Function} fn The function to use to process the cell's raw data
16105      * to return HTML markup for the grid view. The render function is called with
16106      * the following parameters:<ul>
16107      * <li>Data value.</li>
16108      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16109      * <li>css A CSS style string to apply to the table cell.</li>
16110      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16111      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16112      * <li>Row index</li>
16113      * <li>Column index</li>
16114      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16115      */
16116     setRenderer : function(col, fn){
16117         this.config[col].renderer = fn;
16118     },
16119
16120     /**
16121      * Returns the width for the specified column.
16122      * @param {Number} col The column index
16123      * @return {Number}
16124      */
16125     getColumnWidth : function(col){
16126         return this.config[col].width * 1 || this.defaultWidth;
16127     },
16128
16129     /**
16130      * Sets the width for a column.
16131      * @param {Number} col The column index
16132      * @param {Number} width The new width
16133      */
16134     setColumnWidth : function(col, width, suppressEvent){
16135         this.config[col].width = width;
16136         this.totalWidth = null;
16137         if(!suppressEvent){
16138              this.fireEvent("widthchange", this, col, width);
16139         }
16140     },
16141
16142     /**
16143      * Returns the total width of all columns.
16144      * @param {Boolean} includeHidden True to include hidden column widths
16145      * @return {Number}
16146      */
16147     getTotalWidth : function(includeHidden){
16148         if(!this.totalWidth){
16149             this.totalWidth = 0;
16150             for(var i = 0, len = this.config.length; i < len; i++){
16151                 if(includeHidden || !this.isHidden(i)){
16152                     this.totalWidth += this.getColumnWidth(i);
16153                 }
16154             }
16155         }
16156         return this.totalWidth;
16157     },
16158
16159     /**
16160      * Returns the header for the specified column.
16161      * @param {Number} col The column index
16162      * @return {String}
16163      */
16164     getColumnHeader : function(col){
16165         return this.config[col].header;
16166     },
16167
16168     /**
16169      * Sets the header for a column.
16170      * @param {Number} col The column index
16171      * @param {String} header The new header
16172      */
16173     setColumnHeader : function(col, header){
16174         this.config[col].header = header;
16175         this.fireEvent("headerchange", this, col, header);
16176     },
16177
16178     /**
16179      * Returns the tooltip for the specified column.
16180      * @param {Number} col The column index
16181      * @return {String}
16182      */
16183     getColumnTooltip : function(col){
16184             return this.config[col].tooltip;
16185     },
16186     /**
16187      * Sets the tooltip for a column.
16188      * @param {Number} col The column index
16189      * @param {String} tooltip The new tooltip
16190      */
16191     setColumnTooltip : function(col, tooltip){
16192             this.config[col].tooltip = tooltip;
16193     },
16194
16195     /**
16196      * Returns the dataIndex for the specified column.
16197      * @param {Number} col The column index
16198      * @return {Number}
16199      */
16200     getDataIndex : function(col){
16201         return this.config[col].dataIndex;
16202     },
16203
16204     /**
16205      * Sets the dataIndex for a column.
16206      * @param {Number} col The column index
16207      * @param {Number} dataIndex The new dataIndex
16208      */
16209     setDataIndex : function(col, dataIndex){
16210         this.config[col].dataIndex = dataIndex;
16211     },
16212
16213     
16214     
16215     /**
16216      * Returns true if the cell is editable.
16217      * @param {Number} colIndex The column index
16218      * @param {Number} rowIndex The row index
16219      * @return {Boolean}
16220      */
16221     isCellEditable : function(colIndex, rowIndex){
16222         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16223     },
16224
16225     /**
16226      * Returns the editor defined for the cell/column.
16227      * return false or null to disable editing.
16228      * @param {Number} colIndex The column index
16229      * @param {Number} rowIndex The row index
16230      * @return {Object}
16231      */
16232     getCellEditor : function(colIndex, rowIndex){
16233         return this.config[colIndex].editor;
16234     },
16235
16236     /**
16237      * Sets if a column is editable.
16238      * @param {Number} col The column index
16239      * @param {Boolean} editable True if the column is editable
16240      */
16241     setEditable : function(col, editable){
16242         this.config[col].editable = editable;
16243     },
16244
16245
16246     /**
16247      * Returns true if the column is hidden.
16248      * @param {Number} colIndex The column index
16249      * @return {Boolean}
16250      */
16251     isHidden : function(colIndex){
16252         return this.config[colIndex].hidden;
16253     },
16254
16255
16256     /**
16257      * Returns true if the column width cannot be changed
16258      */
16259     isFixed : function(colIndex){
16260         return this.config[colIndex].fixed;
16261     },
16262
16263     /**
16264      * Returns true if the column can be resized
16265      * @return {Boolean}
16266      */
16267     isResizable : function(colIndex){
16268         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16269     },
16270     /**
16271      * Sets if a column is hidden.
16272      * @param {Number} colIndex The column index
16273      * @param {Boolean} hidden True if the column is hidden
16274      */
16275     setHidden : function(colIndex, hidden){
16276         this.config[colIndex].hidden = hidden;
16277         this.totalWidth = null;
16278         this.fireEvent("hiddenchange", this, colIndex, hidden);
16279     },
16280
16281     /**
16282      * Sets the editor for a column.
16283      * @param {Number} col The column index
16284      * @param {Object} editor The editor object
16285      */
16286     setEditor : function(col, editor){
16287         this.config[col].editor = editor;
16288     }
16289 });
16290
16291 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16292         if(typeof value == "string" && value.length < 1){
16293             return "&#160;";
16294         }
16295         return value;
16296 };
16297
16298 // Alias for backwards compatibility
16299 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16300
16301 /**
16302  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16303  * @class Roo.bootstrap.Table.RowSelectionModel
16304  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16305  * It supports multiple selections and keyboard selection/navigation. 
16306  * @constructor
16307  * @param {Object} config
16308  */
16309
16310 Roo.bootstrap.Table.RowSelectionModel = function(config){
16311     Roo.apply(this, config);
16312     this.selections = new Roo.util.MixedCollection(false, function(o){
16313         return o.id;
16314     });
16315
16316     this.last = false;
16317     this.lastActive = false;
16318
16319     this.addEvents({
16320         /**
16321              * @event selectionchange
16322              * Fires when the selection changes
16323              * @param {SelectionModel} this
16324              */
16325             "selectionchange" : true,
16326         /**
16327              * @event afterselectionchange
16328              * Fires after the selection changes (eg. by key press or clicking)
16329              * @param {SelectionModel} this
16330              */
16331             "afterselectionchange" : true,
16332         /**
16333              * @event beforerowselect
16334              * Fires when a row is selected being selected, return false to cancel.
16335              * @param {SelectionModel} this
16336              * @param {Number} rowIndex The selected index
16337              * @param {Boolean} keepExisting False if other selections will be cleared
16338              */
16339             "beforerowselect" : true,
16340         /**
16341              * @event rowselect
16342              * Fires when a row is selected.
16343              * @param {SelectionModel} this
16344              * @param {Number} rowIndex The selected index
16345              * @param {Roo.data.Record} r The record
16346              */
16347             "rowselect" : true,
16348         /**
16349              * @event rowdeselect
16350              * Fires when a row is deselected.
16351              * @param {SelectionModel} this
16352              * @param {Number} rowIndex The selected index
16353              */
16354         "rowdeselect" : true
16355     });
16356     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16357     this.locked = false;
16358 };
16359
16360 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16361     /**
16362      * @cfg {Boolean} singleSelect
16363      * True to allow selection of only one row at a time (defaults to false)
16364      */
16365     singleSelect : false,
16366
16367     // private
16368     initEvents : function(){
16369
16370         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16371             this.grid.on("mousedown", this.handleMouseDown, this);
16372         }else{ // allow click to work like normal
16373             this.grid.on("rowclick", this.handleDragableRowClick, this);
16374         }
16375
16376         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16377             "up" : function(e){
16378                 if(!e.shiftKey){
16379                     this.selectPrevious(e.shiftKey);
16380                 }else if(this.last !== false && this.lastActive !== false){
16381                     var last = this.last;
16382                     this.selectRange(this.last,  this.lastActive-1);
16383                     this.grid.getView().focusRow(this.lastActive);
16384                     if(last !== false){
16385                         this.last = last;
16386                     }
16387                 }else{
16388                     this.selectFirstRow();
16389                 }
16390                 this.fireEvent("afterselectionchange", this);
16391             },
16392             "down" : function(e){
16393                 if(!e.shiftKey){
16394                     this.selectNext(e.shiftKey);
16395                 }else if(this.last !== false && this.lastActive !== false){
16396                     var last = this.last;
16397                     this.selectRange(this.last,  this.lastActive+1);
16398                     this.grid.getView().focusRow(this.lastActive);
16399                     if(last !== false){
16400                         this.last = last;
16401                     }
16402                 }else{
16403                     this.selectFirstRow();
16404                 }
16405                 this.fireEvent("afterselectionchange", this);
16406             },
16407             scope: this
16408         });
16409
16410         var view = this.grid.view;
16411         view.on("refresh", this.onRefresh, this);
16412         view.on("rowupdated", this.onRowUpdated, this);
16413         view.on("rowremoved", this.onRemove, this);
16414     },
16415
16416     // private
16417     onRefresh : function(){
16418         var ds = this.grid.dataSource, i, v = this.grid.view;
16419         var s = this.selections;
16420         s.each(function(r){
16421             if((i = ds.indexOfId(r.id)) != -1){
16422                 v.onRowSelect(i);
16423             }else{
16424                 s.remove(r);
16425             }
16426         });
16427     },
16428
16429     // private
16430     onRemove : function(v, index, r){
16431         this.selections.remove(r);
16432     },
16433
16434     // private
16435     onRowUpdated : function(v, index, r){
16436         if(this.isSelected(r)){
16437             v.onRowSelect(index);
16438         }
16439     },
16440
16441     /**
16442      * Select records.
16443      * @param {Array} records The records to select
16444      * @param {Boolean} keepExisting (optional) True to keep existing selections
16445      */
16446     selectRecords : function(records, keepExisting){
16447         if(!keepExisting){
16448             this.clearSelections();
16449         }
16450         var ds = this.grid.dataSource;
16451         for(var i = 0, len = records.length; i < len; i++){
16452             this.selectRow(ds.indexOf(records[i]), true);
16453         }
16454     },
16455
16456     /**
16457      * Gets the number of selected rows.
16458      * @return {Number}
16459      */
16460     getCount : function(){
16461         return this.selections.length;
16462     },
16463
16464     /**
16465      * Selects the first row in the grid.
16466      */
16467     selectFirstRow : function(){
16468         this.selectRow(0);
16469     },
16470
16471     /**
16472      * Select the last row.
16473      * @param {Boolean} keepExisting (optional) True to keep existing selections
16474      */
16475     selectLastRow : function(keepExisting){
16476         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16477     },
16478
16479     /**
16480      * Selects the row immediately following the last selected row.
16481      * @param {Boolean} keepExisting (optional) True to keep existing selections
16482      */
16483     selectNext : function(keepExisting){
16484         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16485             this.selectRow(this.last+1, keepExisting);
16486             this.grid.getView().focusRow(this.last);
16487         }
16488     },
16489
16490     /**
16491      * Selects the row that precedes the last selected row.
16492      * @param {Boolean} keepExisting (optional) True to keep existing selections
16493      */
16494     selectPrevious : function(keepExisting){
16495         if(this.last){
16496             this.selectRow(this.last-1, keepExisting);
16497             this.grid.getView().focusRow(this.last);
16498         }
16499     },
16500
16501     /**
16502      * Returns the selected records
16503      * @return {Array} Array of selected records
16504      */
16505     getSelections : function(){
16506         return [].concat(this.selections.items);
16507     },
16508
16509     /**
16510      * Returns the first selected record.
16511      * @return {Record}
16512      */
16513     getSelected : function(){
16514         return this.selections.itemAt(0);
16515     },
16516
16517
16518     /**
16519      * Clears all selections.
16520      */
16521     clearSelections : function(fast){
16522         if(this.locked) return;
16523         if(fast !== true){
16524             var ds = this.grid.dataSource;
16525             var s = this.selections;
16526             s.each(function(r){
16527                 this.deselectRow(ds.indexOfId(r.id));
16528             }, this);
16529             s.clear();
16530         }else{
16531             this.selections.clear();
16532         }
16533         this.last = false;
16534     },
16535
16536
16537     /**
16538      * Selects all rows.
16539      */
16540     selectAll : function(){
16541         if(this.locked) return;
16542         this.selections.clear();
16543         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16544             this.selectRow(i, true);
16545         }
16546     },
16547
16548     /**
16549      * Returns True if there is a selection.
16550      * @return {Boolean}
16551      */
16552     hasSelection : function(){
16553         return this.selections.length > 0;
16554     },
16555
16556     /**
16557      * Returns True if the specified row is selected.
16558      * @param {Number/Record} record The record or index of the record to check
16559      * @return {Boolean}
16560      */
16561     isSelected : function(index){
16562         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16563         return (r && this.selections.key(r.id) ? true : false);
16564     },
16565
16566     /**
16567      * Returns True if the specified record id is selected.
16568      * @param {String} id The id of record to check
16569      * @return {Boolean}
16570      */
16571     isIdSelected : function(id){
16572         return (this.selections.key(id) ? true : false);
16573     },
16574
16575     // private
16576     handleMouseDown : function(e, t){
16577         var view = this.grid.getView(), rowIndex;
16578         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16579             return;
16580         };
16581         if(e.shiftKey && this.last !== false){
16582             var last = this.last;
16583             this.selectRange(last, rowIndex, e.ctrlKey);
16584             this.last = last; // reset the last
16585             view.focusRow(rowIndex);
16586         }else{
16587             var isSelected = this.isSelected(rowIndex);
16588             if(e.button !== 0 && isSelected){
16589                 view.focusRow(rowIndex);
16590             }else if(e.ctrlKey && isSelected){
16591                 this.deselectRow(rowIndex);
16592             }else if(!isSelected){
16593                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16594                 view.focusRow(rowIndex);
16595             }
16596         }
16597         this.fireEvent("afterselectionchange", this);
16598     },
16599     // private
16600     handleDragableRowClick :  function(grid, rowIndex, e) 
16601     {
16602         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16603             this.selectRow(rowIndex, false);
16604             grid.view.focusRow(rowIndex);
16605              this.fireEvent("afterselectionchange", this);
16606         }
16607     },
16608     
16609     /**
16610      * Selects multiple rows.
16611      * @param {Array} rows Array of the indexes of the row to select
16612      * @param {Boolean} keepExisting (optional) True to keep existing selections
16613      */
16614     selectRows : function(rows, keepExisting){
16615         if(!keepExisting){
16616             this.clearSelections();
16617         }
16618         for(var i = 0, len = rows.length; i < len; i++){
16619             this.selectRow(rows[i], true);
16620         }
16621     },
16622
16623     /**
16624      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16625      * @param {Number} startRow The index of the first row in the range
16626      * @param {Number} endRow The index of the last row in the range
16627      * @param {Boolean} keepExisting (optional) True to retain existing selections
16628      */
16629     selectRange : function(startRow, endRow, keepExisting){
16630         if(this.locked) return;
16631         if(!keepExisting){
16632             this.clearSelections();
16633         }
16634         if(startRow <= endRow){
16635             for(var i = startRow; i <= endRow; i++){
16636                 this.selectRow(i, true);
16637             }
16638         }else{
16639             for(var i = startRow; i >= endRow; i--){
16640                 this.selectRow(i, true);
16641             }
16642         }
16643     },
16644
16645     /**
16646      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16647      * @param {Number} startRow The index of the first row in the range
16648      * @param {Number} endRow The index of the last row in the range
16649      */
16650     deselectRange : function(startRow, endRow, preventViewNotify){
16651         if(this.locked) return;
16652         for(var i = startRow; i <= endRow; i++){
16653             this.deselectRow(i, preventViewNotify);
16654         }
16655     },
16656
16657     /**
16658      * Selects a row.
16659      * @param {Number} row The index of the row to select
16660      * @param {Boolean} keepExisting (optional) True to keep existing selections
16661      */
16662     selectRow : function(index, keepExisting, preventViewNotify){
16663         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16664         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16665             if(!keepExisting || this.singleSelect){
16666                 this.clearSelections();
16667             }
16668             var r = this.grid.dataSource.getAt(index);
16669             this.selections.add(r);
16670             this.last = this.lastActive = index;
16671             if(!preventViewNotify){
16672                 this.grid.getView().onRowSelect(index);
16673             }
16674             this.fireEvent("rowselect", this, index, r);
16675             this.fireEvent("selectionchange", this);
16676         }
16677     },
16678
16679     /**
16680      * Deselects a row.
16681      * @param {Number} row The index of the row to deselect
16682      */
16683     deselectRow : function(index, preventViewNotify){
16684         if(this.locked) return;
16685         if(this.last == index){
16686             this.last = false;
16687         }
16688         if(this.lastActive == index){
16689             this.lastActive = false;
16690         }
16691         var r = this.grid.dataSource.getAt(index);
16692         this.selections.remove(r);
16693         if(!preventViewNotify){
16694             this.grid.getView().onRowDeselect(index);
16695         }
16696         this.fireEvent("rowdeselect", this, index);
16697         this.fireEvent("selectionchange", this);
16698     },
16699
16700     // private
16701     restoreLast : function(){
16702         if(this._last){
16703             this.last = this._last;
16704         }
16705     },
16706
16707     // private
16708     acceptsNav : function(row, col, cm){
16709         return !cm.isHidden(col) && cm.isCellEditable(col, row);
16710     },
16711
16712     // private
16713     onEditorKey : function(field, e){
16714         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16715         if(k == e.TAB){
16716             e.stopEvent();
16717             ed.completeEdit();
16718             if(e.shiftKey){
16719                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16720             }else{
16721                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16722             }
16723         }else if(k == e.ENTER && !e.ctrlKey){
16724             e.stopEvent();
16725             ed.completeEdit();
16726             if(e.shiftKey){
16727                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16728             }else{
16729                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16730             }
16731         }else if(k == e.ESC){
16732             ed.cancelEdit();
16733         }
16734         if(newCell){
16735             g.startEditing(newCell[0], newCell[1]);
16736         }
16737     }
16738 });/*
16739  * - LGPL
16740  *
16741  * element
16742  * 
16743  */
16744
16745 /**
16746  * @class Roo.bootstrap.MessageBar
16747  * @extends Roo.bootstrap.Component
16748  * Bootstrap MessageBar class
16749  * @cfg {String} html contents of the MessageBar
16750  * @cfg {String} weight (info | success | warning | danger) default info
16751  * @cfg {String} beforeClass insert the bar before the given class
16752  * @cfg {Boolean} closable (true | false) default false
16753  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16754  * 
16755  * @constructor
16756  * Create a new Element
16757  * @param {Object} config The config object
16758  */
16759
16760 Roo.bootstrap.MessageBar = function(config){
16761     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16762 };
16763
16764 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
16765     
16766     html: '',
16767     weight: 'info',
16768     closable: false,
16769     fixed: false,
16770     beforeClass: 'bootstrap-sticky-wrap',
16771     
16772     getAutoCreate : function(){
16773         
16774         var cfg = {
16775             tag: 'div',
16776             cls: 'alert alert-dismissable alert-' + this.weight,
16777             cn: [
16778                 {
16779                     tag: 'span',
16780                     cls: 'message',
16781                     html: this.html || ''
16782                 }
16783             ]
16784         }
16785         
16786         if(this.fixed){
16787             cfg.cls += ' alert-messages-fixed';
16788         }
16789         
16790         if(this.closable){
16791             cfg.cn.push({
16792                 tag: 'button',
16793                 cls: 'close',
16794                 html: 'x'
16795             });
16796         }
16797         
16798         return cfg;
16799     },
16800     
16801     onRender : function(ct, position)
16802     {
16803         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16804         
16805         if(!this.el){
16806             var cfg = Roo.apply({},  this.getAutoCreate());
16807             cfg.id = Roo.id();
16808             
16809             if (this.cls) {
16810                 cfg.cls += ' ' + this.cls;
16811             }
16812             if (this.style) {
16813                 cfg.style = this.style;
16814             }
16815             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16816             
16817             this.el.setVisibilityMode(Roo.Element.DISPLAY);
16818         }
16819         
16820         this.el.select('>button.close').on('click', this.hide, this);
16821         
16822     },
16823     
16824     show : function()
16825     {
16826         if (!this.rendered) {
16827             this.render();
16828         }
16829         
16830         this.el.show();
16831         
16832         this.fireEvent('show', this);
16833         
16834     },
16835     
16836     hide : function()
16837     {
16838         if (!this.rendered) {
16839             this.render();
16840         }
16841         
16842         this.el.hide();
16843         
16844         this.fireEvent('hide', this);
16845     },
16846     
16847     update : function()
16848     {
16849 //        var e = this.el.dom.firstChild;
16850 //        
16851 //        if(this.closable){
16852 //            e = e.nextSibling;
16853 //        }
16854 //        
16855 //        e.data = this.html || '';
16856
16857         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
16858     }
16859    
16860 });
16861
16862  
16863
16864