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 (!cfg.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  * @cfg {Boolean} sidebar has side bar
2657  * @cfg {Boolean} bar is a bar?
2658  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2659  * @cfg {String} brand what is brand
2660  * @cfg {Boolean} inverse is inverted color
2661  * @cfg {String} type (nav | pills | tabs)
2662  * @cfg {Boolean} arrangement stacked | justified
2663  * @cfg {String} align (left | right) alignment
2664  * @cfg {String} brand_href href of the brand
2665  * @cfg {Boolean} main (true|false) main nav bar? default false
2666  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2667  *
2668  * 
2669  * @constructor
2670  * Create a new Navbar
2671  * @param {Object} config The config object
2672  */
2673
2674
2675 Roo.bootstrap.Navbar = function(config){
2676     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2677    
2678     
2679 };
2680
2681 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2682     
2683     sidebar: false,
2684     
2685     bar: false,
2686     brand: '',
2687     inverse: false,
2688     position: '',
2689     align : false,
2690     type: 'nav',
2691     arrangement: '',
2692     brand_href: false,
2693     main : false,
2694     loadMask : false,
2695     
2696     
2697     // private
2698     navItems : false,
2699     
2700     getAutoCreate : function(){
2701         var cfg = {
2702             cls : 'navbar'
2703         };
2704         
2705         if (this.sidebar === true) {
2706             cfg = {
2707                 tag: 'div',
2708                 cls: 'sidebar-nav'
2709             };
2710             return cfg;
2711         }
2712         
2713         if (this.bar === true) {
2714             cfg = {
2715                 tag: 'nav',
2716                 cls: 'navbar',
2717                 role: 'navigation',
2718                 cn: [
2719                     {
2720                         tag: 'div',
2721                         cls: 'navbar-header',
2722                         cn: [
2723                             {
2724                             tag: 'button',
2725                             type: 'button',
2726                             cls: 'navbar-toggle',
2727                             'data-toggle': 'collapse',
2728                             cn: [
2729                                 {
2730                                     tag: 'span',
2731                                     cls: 'sr-only',
2732                                     html: 'Toggle navigation'
2733                                 },
2734                                 {
2735                                     tag: 'span',
2736                                     cls: 'icon-bar'
2737                                 },
2738                                 {
2739                                     tag: 'span',
2740                                     cls: 'icon-bar'
2741                                 },
2742                                 {
2743                                     tag: 'span',
2744                                     cls: 'icon-bar'
2745                                 }
2746                             ]
2747                             }
2748                         ]
2749                     },
2750                     {
2751                     tag: 'div',
2752                     cls: 'collapse navbar-collapse'
2753                     }
2754                 ]
2755             };
2756             
2757             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2758             
2759             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2760                 cfg.cls += ' navbar-' + this.position;
2761                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2762             }
2763             
2764             if (this.brand !== '') {
2765                 cfg.cn[0].cn.push({
2766                     tag: 'a',
2767                     href: this.brand_href ? this.brand_href : '#',
2768                     cls: 'navbar-brand',
2769                     cn: [
2770                     this.brand
2771                     ]
2772                 });
2773             }
2774             
2775             if(this.main){
2776                 cfg.cls += ' main-nav';
2777             }
2778             
2779             
2780             return cfg;
2781         
2782         } else if (this.bar === false) {
2783             
2784         } else {
2785             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2786         }
2787         
2788         cfg.cn = [
2789             {
2790                 cls: 'nav',
2791                 tag : 'ul'
2792             }
2793         ];
2794         
2795         if (['tabs','pills'].indexOf(this.type)!==-1) {
2796             cfg.cn[0].cls += ' nav-' + this.type
2797         } else {
2798             if (this.type!=='nav') {
2799             Roo.log('nav type must be nav/tabs/pills')
2800             }
2801             cfg.cn[0].cls += ' navbar-nav'
2802         }
2803         
2804         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2805             cfg.cn[0].cls += ' nav-' + this.arrangement;
2806         }
2807         
2808         if (this.align === 'right') {
2809             cfg.cn[0].cls += ' navbar-right';
2810         }
2811         if (this.inverse) {
2812             cfg.cls += ' navbar-inverse';
2813             
2814         }
2815         
2816         
2817         return cfg;
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.bar === true) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     }
2864     
2865     
2866     
2867 });
2868
2869
2870
2871  
2872
2873  /*
2874  * - LGPL
2875  *
2876  * nav group
2877  * 
2878  */
2879
2880 /**
2881  * @class Roo.bootstrap.NavGroup
2882  * @extends Roo.bootstrap.Component
2883  * Bootstrap NavGroup class
2884  * @cfg {String} align left | right
2885  * @cfg {Boolean} inverse false | true
2886  * @cfg {String} type (nav|pills|tab) default nav
2887  * @cfg {String} navId - reference Id for navbar.
2888
2889  * 
2890  * @constructor
2891  * Create a new nav group
2892  * @param {Object} config The config object
2893  */
2894
2895 Roo.bootstrap.NavGroup = function(config){
2896     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2897     this.navItems = [];
2898     Roo.bootstrap.NavGroup.register(this);
2899      this.addEvents({
2900         /**
2901              * @event changed
2902              * Fires when the active item changes
2903              * @param {Roo.bootstrap.NavGroup} this
2904              * @param {Roo.bootstrap.Navbar.Item} item The item selected
2905              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
2906          */
2907         'changed': true
2908      });
2909     
2910 };
2911
2912 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2913     
2914     align: '',
2915     inverse: false,
2916     form: false,
2917     type: 'nav',
2918     navId : '',
2919     // private
2920     
2921     navItems : false,
2922     
2923     getAutoCreate : function()
2924     {
2925         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2926         
2927         cfg = {
2928             tag : 'ul',
2929             cls: 'nav' 
2930         }
2931         
2932         if (['tabs','pills'].indexOf(this.type)!==-1) {
2933             cfg.cls += ' nav-' + this.type
2934         } else {
2935             if (this.type!=='nav') {
2936                 Roo.log('nav type must be nav/tabs/pills')
2937             }
2938             cfg.cls += ' navbar-nav'
2939         }
2940         
2941         if (this.parent().sidebar === true) {
2942             cfg = {
2943                 tag: 'ul',
2944                 cls: 'dashboard-menu'
2945             }
2946             
2947             return cfg;
2948         }
2949         
2950         if (this.form === true) {
2951             cfg = {
2952                 tag: 'form',
2953                 cls: 'navbar-form'
2954             }
2955             
2956             if (this.align === 'right') {
2957                 cfg.cls += ' navbar-right';
2958             } else {
2959                 cfg.cls += ' navbar-left';
2960             }
2961         }
2962         
2963         if (this.align === 'right') {
2964             cfg.cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     },
2975     
2976     setActiveItem : function(item)
2977     {
2978         var prev = false;
2979         Roo.each(this.navItems, function(v){
2980             if (v == item) {
2981                 return ;
2982             }
2983             if (v.isActive()) {
2984                 v.setActive(false, true);
2985                 prev = v;
2986                 
2987             }
2988             
2989         });
2990
2991         item.setActive(true, true);
2992         this.fireEvent('changed', this, item, prev);
2993         
2994         
2995     },
2996     
2997     
2998     register : function(item)
2999     {
3000         this.navItems.push( item);
3001         item.navId = this.navId;
3002     
3003     },
3004     getNavItem: function(tabId)
3005     {
3006         var ret = false;
3007         Roo.each(this.navItems, function(e) {
3008             if (e.tabId == tabId) {
3009                ret =  e;
3010                return false;
3011             }
3012             return true;
3013             
3014         });
3015         return ret;
3016     }
3017 });
3018
3019  
3020 Roo.apply(Roo.bootstrap.NavGroup, {
3021     
3022     groups: {},
3023     
3024     register : function(navgrp)
3025     {
3026         this.groups[navgrp.navId] = navgrp;
3027         
3028     },
3029     get: function(navId) {
3030         return this.groups[navId];
3031     }
3032     
3033     
3034     
3035 });
3036
3037  /*
3038  * - LGPL
3039  *
3040  * row
3041  * 
3042  */
3043
3044 /**
3045  * @class Roo.bootstrap.Navbar.Item
3046  * @extends Roo.bootstrap.Component
3047  * Bootstrap Navbar.Button class
3048  * @cfg {String} href  link to
3049  * @cfg {String} html content of button
3050  * @cfg {String} badge text inside badge
3051  * @cfg {String} glyphicon name of glyphicon
3052  * @cfg {String} icon name of font awesome icon
3053  * @cfg {Boolean} active Is item active
3054  * @cfg {Boolean} preventDefault (true | false) default false
3055  * @cfg {String} tabId the tab that this item activates.
3056   
3057  * @constructor
3058  * Create a new Navbar Button
3059  * @param {Object} config The config object
3060  */
3061 Roo.bootstrap.Navbar.Item = function(config){
3062     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3063     this.addEvents({
3064         // raw events
3065         /**
3066          * @event click
3067          * The raw click event for the entire grid.
3068          * @param {Roo.EventObject} e
3069          */
3070         "click" : true,
3071          /**
3072             * @event changed
3073             * Fires when the active item active state changes
3074             * @param {Roo.bootstrap.Navbar.Item} this
3075             * @param {boolean} state the new state
3076              
3077          */
3078         'changed': true
3079     });
3080    
3081 };
3082
3083 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3084     
3085     href: false,
3086     html: '',
3087     badge: '',
3088     icon: false,
3089     glyphicon: false,
3090     active: false,
3091     preventDefault : false,
3092     tabId : false,
3093     
3094     getAutoCreate : function(){
3095         
3096         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3097         
3098         if (this.parent().parent().sidebar === true) {
3099             cfg = {
3100                 tag: 'li',
3101                 cls: '',
3102                 cn: [
3103                     {
3104                         tag: 'p',
3105                         cls: ''
3106                     }
3107                 ]
3108             }
3109             
3110             if (this.html) {
3111                 cfg.cn[0].html = this.html;
3112             }
3113             
3114             if (this.active) {
3115                 this.cls += ' active';
3116             }
3117             
3118             if (this.menu) {
3119                 cfg.cn[0].cls += ' dropdown-toggle';
3120                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3121             }
3122             
3123             if (this.href) {
3124                 cfg.cn[0].tag = 'a',
3125                 cfg.cn[0].href = this.href;
3126             }
3127             
3128             if (this.glyphicon) {
3129                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3130             }
3131             
3132             if (this.icon) {
3133                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3134             }
3135             
3136             return cfg;
3137         }
3138         
3139         cfg = {
3140             tag: 'li',
3141             cls: 'nav-item'
3142         }
3143         
3144         if (this.active) {
3145             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3146         }
3147             
3148         cfg.cn = [
3149             {
3150                 tag: 'p',
3151                 html: 'Text'
3152             }
3153         ];
3154         
3155         if (this.glyphicon) {
3156             if(cfg.html){cfg.html = ' ' + this.html};
3157             cfg.cn=[
3158                 {
3159                     tag: 'span',
3160                     cls: 'glyphicon glyphicon-' + this.glyphicon
3161                 }
3162             ];
3163         }
3164         
3165         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3166         
3167         if (this.menu) {
3168             cfg.cn[0].tag='a';
3169             cfg.cn[0].href='#';
3170             cfg.cn[0].html += " <span class='caret'></span>";
3171         //}else if (!this.href) {
3172         //    cfg.cn[0].tag='p';
3173         //    cfg.cn[0].cls='navbar-text';
3174         } else {
3175             cfg.cn[0].tag='a';
3176             cfg.cn[0].href=this.href||'#';
3177             cfg.cn[0].html=this.html;
3178         }
3179         
3180         if (this.badge !== '') {
3181             
3182             cfg.cn[0].cn=[
3183                 cfg.cn[0].html + ' ',
3184                 {
3185                     tag: 'span',
3186                     cls: 'badge',
3187                     html: this.badge
3188                 }
3189             ];
3190             cfg.cn[0].html=''
3191         }
3192          
3193         if (this.icon) {
3194             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3195         }
3196         
3197         return cfg;
3198     },
3199     initEvents: function() {
3200        // Roo.log('init events?');
3201        // Roo.log(this.el.dom);
3202         this.el.select('a',true).on('click', this.onClick, this);
3203         // at this point parent should be available..
3204         this.parent().register(this);
3205     },
3206     
3207     onClick : function(e)
3208     {
3209         if(this.preventDefault){
3210             e.preventDefault();
3211         }
3212         
3213         if(this.fireEvent('click', this, e) === false){
3214             return;
3215         };
3216         
3217         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3218              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3219                 this.parent().setActiveItem(this);
3220             }
3221             
3222             
3223             
3224         } 
3225     },
3226     
3227     isActive: function () {
3228         return this.active
3229     },
3230     setActive : function(state, fire)
3231     {
3232         this.active = state;
3233         if (!state ) {
3234             this.el.removeClass('active');
3235         } else if (!this.el.hasClass('active')) {
3236             this.el.addClass('active');
3237         }
3238         if (fire) {
3239             this.fireEvent('changed', this, state);
3240         }
3241         
3242         
3243     }
3244      // this should not be here...
3245  
3246 });
3247  
3248
3249  /*
3250  * - LGPL
3251  *
3252  * row
3253  * 
3254  */
3255
3256 /**
3257  * @class Roo.bootstrap.Row
3258  * @extends Roo.bootstrap.Component
3259  * Bootstrap Row class (contains columns...)
3260  * 
3261  * @constructor
3262  * Create a new Row
3263  * @param {Object} config The config object
3264  */
3265
3266 Roo.bootstrap.Row = function(config){
3267     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3268 };
3269
3270 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3271     
3272     getAutoCreate : function(){
3273        return {
3274             cls: 'row clearfix'
3275        };
3276     }
3277     
3278     
3279 });
3280
3281  
3282
3283  /*
3284  * - LGPL
3285  *
3286  * element
3287  * 
3288  */
3289
3290 /**
3291  * @class Roo.bootstrap.Element
3292  * @extends Roo.bootstrap.Component
3293  * Bootstrap Element class
3294  * @cfg {String} html contents of the element
3295  * @cfg {String} tag tag of the element
3296  * @cfg {String} cls class of the element
3297  * 
3298  * @constructor
3299  * Create a new Element
3300  * @param {Object} config The config object
3301  */
3302
3303 Roo.bootstrap.Element = function(config){
3304     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3305 };
3306
3307 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3308     
3309     tag: 'div',
3310     cls: '',
3311     html: '',
3312      
3313     
3314     getAutoCreate : function(){
3315         
3316         var cfg = {
3317             tag: this.tag,
3318             cls: this.cls,
3319             html: this.html
3320         }
3321         
3322         
3323         
3324         return cfg;
3325     }
3326    
3327 });
3328
3329  
3330
3331  /*
3332  * - LGPL
3333  *
3334  * pagination
3335  * 
3336  */
3337
3338 /**
3339  * @class Roo.bootstrap.Pagination
3340  * @extends Roo.bootstrap.Component
3341  * Bootstrap Pagination class
3342  * @cfg {String} size xs | sm | md | lg
3343  * @cfg {Boolean} inverse false | true
3344  * 
3345  * @constructor
3346  * Create a new Pagination
3347  * @param {Object} config The config object
3348  */
3349
3350 Roo.bootstrap.Pagination = function(config){
3351     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3352 };
3353
3354 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3355     
3356     cls: false,
3357     size: false,
3358     inverse: false,
3359     
3360     getAutoCreate : function(){
3361         var cfg = {
3362             tag: 'ul',
3363                 cls: 'pagination'
3364         };
3365         if (this.inverse) {
3366             cfg.cls += ' inverse';
3367         }
3368         if (this.html) {
3369             cfg.html=this.html;
3370         }
3371         if (this.cls) {
3372             cfg.cls += " " + this.cls;
3373         }
3374         return cfg;
3375     }
3376    
3377 });
3378
3379  
3380
3381  /*
3382  * - LGPL
3383  *
3384  * Pagination item
3385  * 
3386  */
3387
3388
3389 /**
3390  * @class Roo.bootstrap.PaginationItem
3391  * @extends Roo.bootstrap.Component
3392  * Bootstrap PaginationItem class
3393  * @cfg {String} html text
3394  * @cfg {String} href the link
3395  * @cfg {Boolean} preventDefault (true | false) default true
3396  * @cfg {Boolean} active (true | false) default false
3397  * 
3398  * 
3399  * @constructor
3400  * Create a new PaginationItem
3401  * @param {Object} config The config object
3402  */
3403
3404
3405 Roo.bootstrap.PaginationItem = function(config){
3406     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3407     this.addEvents({
3408         // raw events
3409         /**
3410          * @event click
3411          * The raw click event for the entire grid.
3412          * @param {Roo.EventObject} e
3413          */
3414         "click" : true
3415     });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3419     
3420     href : false,
3421     html : false,
3422     preventDefault: true,
3423     active : false,
3424     cls : false,
3425     
3426     getAutoCreate : function(){
3427         var cfg= {
3428             tag: 'li',
3429             cn: [
3430                 {
3431                     tag : 'a',
3432                     href : this.href ? this.href : '#',
3433                     html : this.html ? this.html : ''
3434                 }
3435             ]
3436         };
3437         
3438         if(this.cls){
3439             cfg.cls = this.cls;
3440         }
3441         
3442         if(this.active){
3443             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3444         }
3445         
3446         return cfg;
3447     },
3448     
3449     initEvents: function() {
3450         
3451         this.el.on('click', this.onClick, this);
3452         
3453     },
3454     onClick : function(e)
3455     {
3456         Roo.log('PaginationItem on click ');
3457         if(this.preventDefault){
3458             e.preventDefault();
3459         }
3460         
3461         this.fireEvent('click', this, e);
3462     }
3463    
3464 });
3465
3466  
3467
3468  /*
3469  * - LGPL
3470  *
3471  * slider
3472  * 
3473  */
3474
3475
3476 /**
3477  * @class Roo.bootstrap.Slider
3478  * @extends Roo.bootstrap.Component
3479  * Bootstrap Slider class
3480  *    
3481  * @constructor
3482  * Create a new Slider
3483  * @param {Object} config The config object
3484  */
3485
3486 Roo.bootstrap.Slider = function(config){
3487     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3488 };
3489
3490 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3491     
3492     getAutoCreate : function(){
3493         
3494         var cfg = {
3495             tag: 'div',
3496             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3497             cn: [
3498                 {
3499                     tag: 'a',
3500                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3501                 }
3502             ]
3503         }
3504         
3505         return cfg;
3506     }
3507    
3508 });
3509
3510  /*
3511  * - LGPL
3512  *
3513  * table
3514  * 
3515  */
3516
3517 /**
3518  * @class Roo.bootstrap.Table
3519  * @extends Roo.bootstrap.Component
3520  * Bootstrap Table class
3521  * @cfg {String} cls table class
3522  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3523  * @cfg {String} bgcolor Specifies the background color for a table
3524  * @cfg {Number} border Specifies whether the table cells should have borders or not
3525  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3526  * @cfg {Number} cellspacing Specifies the space between cells
3527  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3528  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3529  * @cfg {String} sortable Specifies that the table should be sortable
3530  * @cfg {String} summary Specifies a summary of the content of a table
3531  * @cfg {Number} width Specifies the width of a table
3532  * 
3533  * @cfg {boolean} striped Should the rows be alternative striped
3534  * @cfg {boolean} bordered Add borders to the table
3535  * @cfg {boolean} hover Add hover highlighting
3536  * @cfg {boolean} condensed Format condensed
3537  * @cfg {boolean} responsive Format condensed
3538  *
3539  
3540  
3541  * 
3542  * @constructor
3543  * Create a new Table
3544  * @param {Object} config The config object
3545  */
3546
3547 Roo.bootstrap.Table = function(config){
3548     Roo.bootstrap.Table.superclass.constructor.call(this, config);
3549     
3550     if (this.sm) {
3551         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3552         this.sm = this.selModel;
3553         this.sm.xmodule = this.xmodule || false;
3554     }
3555     if (this.cm && typeof(this.cm.config) == 'undefined') {
3556         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
3557         this.cm = this.colModel;
3558         this.cm.xmodule = this.xmodule || false;
3559     }
3560     if (this.store) {
3561         this.store= Roo.factory(this.store, Roo.data);
3562         this.ds = this.store;
3563         this.ds.xmodule = this.xmodule || false;
3564          
3565     }
3566 };
3567
3568 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
3569     
3570     cls: false,
3571     align: false,
3572     bgcolor: false,
3573     border: false,
3574     cellpadding: false,
3575     cellspacing: false,
3576     frame: false,
3577     rules: false,
3578     sortable: false,
3579     summary: false,
3580     width: false,
3581     striped : false,
3582     bordered: false,
3583     hover:  false,
3584     condensed : false,
3585     responsive : false,
3586     sm : false,
3587     cm : false,
3588     store : false,
3589     
3590     getAutoCreate : function(){
3591         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3592         
3593         cfg = {
3594             tag: 'table',
3595             cls : 'table',
3596             cn : []
3597         }
3598             
3599         if (this.striped) {
3600             cfg.cls += ' table-striped';
3601         }
3602         if (this.hover) {
3603             cfg.cls += ' table-hover';
3604         }
3605         if (this.bordered) {
3606             cfg.cls += ' table-bordered';
3607         }
3608         if (this.condensed) {
3609             cfg.cls += ' table-condensed';
3610         }
3611         if (this.responsive) {
3612             cfg.cls += ' table-responsive';
3613         }
3614         
3615           
3616         
3617         
3618         if (this.cls) {
3619             cfg.cls+=  ' ' +this.cls;
3620         }
3621         
3622         // this lot should be simplifed...
3623         
3624         if (this.align) {
3625             cfg.align=this.align;
3626         }
3627         if (this.bgcolor) {
3628             cfg.bgcolor=this.bgcolor;
3629         }
3630         if (this.border) {
3631             cfg.border=this.border;
3632         }
3633         if (this.cellpadding) {
3634             cfg.cellpadding=this.cellpadding;
3635         }
3636         if (this.cellspacing) {
3637             cfg.cellspacing=this.cellspacing;
3638         }
3639         if (this.frame) {
3640             cfg.frame=this.frame;
3641         }
3642         if (this.rules) {
3643             cfg.rules=this.rules;
3644         }
3645         if (this.sortable) {
3646             cfg.sortable=this.sortable;
3647         }
3648         if (this.summary) {
3649             cfg.summary=this.summary;
3650         }
3651         if (this.width) {
3652             cfg.width=this.width;
3653         }
3654         
3655         if(this.store || this.cm){
3656             cfg.cn.push(this.renderHeader());
3657             cfg.cn.push(this.renderBody());
3658             cfg.cn.push(this.renderFooter());
3659             
3660             cfg.cls+=  ' TableGrid';
3661         }
3662         
3663         return cfg;
3664     },
3665 //    
3666 //    initTableGrid : function()
3667 //    {
3668 //        var cfg = {};
3669 //        
3670 //        var header = {
3671 //            tag: 'thead',
3672 //            cn : []
3673 //        };
3674 //        
3675 //        var cm = this.cm;
3676 //        
3677 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3678 //            header.cn.push({
3679 //                tag: 'th',
3680 //                html: cm.getColumnHeader(i)
3681 //            })
3682 //        }
3683 //        
3684 //        cfg.push(header);
3685 //        
3686 //        return cfg;
3687 //        
3688 //        
3689 //    },
3690     
3691     initEvents : function()
3692     {   
3693         if(!this.store || !this.cm){
3694             return;
3695         }
3696         
3697         Roo.log('initEvents with ds!!!!');
3698         
3699         var _this = this;
3700         
3701         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3702             e.on('click', _this.sort, _this);
3703         });
3704 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3705 //        this.maskEl.enableDisplayMode("block");
3706 //        this.maskEl.show();
3707         
3708         this.store.on('load', this.onLoad, this);
3709         this.store.on('beforeload', this.onBeforeLoad, this);
3710         
3711         this.store.load();
3712         
3713         
3714         
3715     },
3716     
3717     sort : function(e,el)
3718     {
3719         var col = Roo.get(el)
3720         
3721         if(!col.hasClass('sortable')){
3722             return;
3723         }
3724         
3725         var sort = col.attr('sort');
3726         var dir = 'ASC';
3727         
3728         if(col.hasClass('glyphicon-arrow-up')){
3729             dir = 'DESC';
3730         }
3731         
3732         this.store.sortInfo = {field : sort, direction : dir};
3733         
3734         this.store.load();
3735     },
3736     
3737     renderHeader : function()
3738     {
3739         var header = {
3740             tag: 'thead',
3741             cn : []
3742         };
3743         
3744         var cm = this.cm;
3745         
3746         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3747             
3748             var config = cm.config[i];
3749             
3750             var c = {
3751                 tag: 'th',
3752                 html: cm.getColumnHeader(i)
3753             };
3754             
3755             if(typeof(config.dataIndex) != 'undefined'){
3756                 c.sort = config.dataIndex;
3757             }
3758             
3759             if(typeof(config.sortable) != 'undefined' && config.sortable){
3760                 c.cls = 'sortable';
3761             }
3762             
3763             if(typeof(config.width) != 'undefined'){
3764                 c.style = 'width:' + config.width + 'px';
3765             }
3766             
3767             header.cn.push(c)
3768         }
3769         
3770         return header;
3771     },
3772     
3773     renderBody : function()
3774     {
3775         var body = {
3776             tag: 'tbody',
3777             cn : []
3778         };
3779         
3780         return body;
3781     },
3782     
3783     renderFooter : function()
3784     {
3785         var footer = {
3786             tag: 'tfoot',
3787             cn : []
3788         };
3789         
3790         return footer;
3791     },
3792     
3793     onLoad : function()
3794     {
3795         Roo.log('ds onload');
3796         
3797         var _this = this;
3798         var cm = this.cm;
3799         
3800         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3801             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3802             
3803             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3804                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3805             }
3806             
3807             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3808                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3809             }
3810         });
3811         
3812         var tbody = this.el.select('tbody', true).first();
3813         
3814         var renders = [];
3815         
3816         if(this.store.getCount() > 0){
3817             this.store.data.each(function(d){
3818                 var row = {
3819                     tag : 'tr',
3820                     cn : []
3821                 };
3822                 
3823                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3824                     var renderer = cm.getRenderer(i);
3825                     var config = cm.config[i];
3826                     var value = '';
3827                     var id = Roo.id();
3828                     
3829                     if(typeof(renderer) !== 'undefined'){
3830                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3831                     }
3832                     
3833                     if(typeof(value) === 'object'){
3834                         renders.push({
3835                             id : id,
3836                             cfg : value 
3837                         })
3838                     }
3839                     
3840                     var td = {
3841                         tag: 'td',
3842                         id: id,
3843                         html: (typeof(value) === 'object') ? '' : value
3844                     };
3845                     
3846                     if(typeof(config.width) != 'undefined'){
3847                         td.style = 'width:' +  config.width + 'px';
3848                     }
3849                     
3850                     row.cn.push(td);
3851                    
3852                 }
3853                 
3854                 tbody.createChild(row);
3855                 
3856             });
3857         }
3858         
3859         
3860         if(renders.length){
3861             var _this = this;
3862             Roo.each(renders, function(r){
3863                 _this.renderColumn(r);
3864             })
3865         }
3866 //        
3867 //        if(this.loadMask){
3868 //            this.maskEl.hide();
3869 //        }
3870     },
3871     
3872     onBeforeLoad : function()
3873     {
3874         Roo.log('ds onBeforeLoad');
3875         
3876         this.clear();
3877         
3878 //        if(this.loadMask){
3879 //            this.maskEl.show();
3880 //        }
3881     },
3882     
3883     clear : function()
3884     {
3885         this.el.select('tbody', true).first().dom.innerHTML = '';
3886     },
3887     
3888     getSelectionModel : function(){
3889         if(!this.selModel){
3890             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3891         }
3892         return this.selModel;
3893     },
3894     
3895     renderColumn : function(r)
3896     {
3897         var _this = this;
3898         r.cfg.render(Roo.get(r.id));
3899         
3900         if(r.cfg.cn){
3901             Roo.each(r.cfg.cn, function(c){
3902                 var child = {
3903                     id: r.id,
3904                     cfg: c
3905                 }
3906                 _this.renderColumn(child);
3907             })
3908         }
3909     }
3910    
3911 });
3912
3913  
3914
3915  /*
3916  * - LGPL
3917  *
3918  * table cell
3919  * 
3920  */
3921
3922 /**
3923  * @class Roo.bootstrap.TableCell
3924  * @extends Roo.bootstrap.Component
3925  * Bootstrap TableCell class
3926  * @cfg {String} html cell contain text
3927  * @cfg {String} cls cell class
3928  * @cfg {String} tag cell tag (td|th) default td
3929  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3930  * @cfg {String} align Aligns the content in a cell
3931  * @cfg {String} axis Categorizes cells
3932  * @cfg {String} bgcolor Specifies the background color of a cell
3933  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3934  * @cfg {Number} colspan Specifies the number of columns a cell should span
3935  * @cfg {String} headers Specifies one or more header cells a cell is related to
3936  * @cfg {Number} height Sets the height of a cell
3937  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3938  * @cfg {Number} rowspan Sets the number of rows a cell should span
3939  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3940  * @cfg {String} valign Vertical aligns the content in a cell
3941  * @cfg {Number} width Specifies the width of a cell
3942  * 
3943  * @constructor
3944  * Create a new TableCell
3945  * @param {Object} config The config object
3946  */
3947
3948 Roo.bootstrap.TableCell = function(config){
3949     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3950 };
3951
3952 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3953     
3954     html: false,
3955     cls: false,
3956     tag: false,
3957     abbr: false,
3958     align: false,
3959     axis: false,
3960     bgcolor: false,
3961     charoff: false,
3962     colspan: false,
3963     headers: false,
3964     height: false,
3965     nowrap: false,
3966     rowspan: false,
3967     scope: false,
3968     valign: false,
3969     width: false,
3970     
3971     
3972     getAutoCreate : function(){
3973         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3974         
3975         cfg = {
3976             tag: 'td'
3977         }
3978         
3979         if(this.tag){
3980             cfg.tag = this.tag;
3981         }
3982         
3983         if (this.html) {
3984             cfg.html=this.html
3985         }
3986         if (this.cls) {
3987             cfg.cls=this.cls
3988         }
3989         if (this.abbr) {
3990             cfg.abbr=this.abbr
3991         }
3992         if (this.align) {
3993             cfg.align=this.align
3994         }
3995         if (this.axis) {
3996             cfg.axis=this.axis
3997         }
3998         if (this.bgcolor) {
3999             cfg.bgcolor=this.bgcolor
4000         }
4001         if (this.charoff) {
4002             cfg.charoff=this.charoff
4003         }
4004         if (this.colspan) {
4005             cfg.colspan=this.colspan
4006         }
4007         if (this.headers) {
4008             cfg.headers=this.headers
4009         }
4010         if (this.height) {
4011             cfg.height=this.height
4012         }
4013         if (this.nowrap) {
4014             cfg.nowrap=this.nowrap
4015         }
4016         if (this.rowspan) {
4017             cfg.rowspan=this.rowspan
4018         }
4019         if (this.scope) {
4020             cfg.scope=this.scope
4021         }
4022         if (this.valign) {
4023             cfg.valign=this.valign
4024         }
4025         if (this.width) {
4026             cfg.width=this.width
4027         }
4028         
4029         
4030         return cfg;
4031     }
4032    
4033 });
4034
4035  
4036
4037  /*
4038  * - LGPL
4039  *
4040  * table row
4041  * 
4042  */
4043
4044 /**
4045  * @class Roo.bootstrap.TableRow
4046  * @extends Roo.bootstrap.Component
4047  * Bootstrap TableRow class
4048  * @cfg {String} cls row class
4049  * @cfg {String} align Aligns the content in a table row
4050  * @cfg {String} bgcolor Specifies a background color for a table row
4051  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4052  * @cfg {String} valign Vertical aligns the content in a table row
4053  * 
4054  * @constructor
4055  * Create a new TableRow
4056  * @param {Object} config The config object
4057  */
4058
4059 Roo.bootstrap.TableRow = function(config){
4060     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4061 };
4062
4063 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4064     
4065     cls: false,
4066     align: false,
4067     bgcolor: false,
4068     charoff: false,
4069     valign: false,
4070     
4071     getAutoCreate : function(){
4072         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4073         
4074         cfg = {
4075             tag: 'tr'
4076         }
4077             
4078         if(this.cls){
4079             cfg.cls = this.cls;
4080         }
4081         if(this.align){
4082             cfg.align = this.align;
4083         }
4084         if(this.bgcolor){
4085             cfg.bgcolor = this.bgcolor;
4086         }
4087         if(this.charoff){
4088             cfg.charoff = this.charoff;
4089         }
4090         if(this.valign){
4091             cfg.valign = this.valign;
4092         }
4093         
4094         return cfg;
4095     }
4096    
4097 });
4098
4099  
4100
4101  /*
4102  * - LGPL
4103  *
4104  * table body
4105  * 
4106  */
4107
4108 /**
4109  * @class Roo.bootstrap.TableBody
4110  * @extends Roo.bootstrap.Component
4111  * Bootstrap TableBody class
4112  * @cfg {String} cls element class
4113  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4114  * @cfg {String} align Aligns the content inside the element
4115  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4116  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4117  * 
4118  * @constructor
4119  * Create a new TableBody
4120  * @param {Object} config The config object
4121  */
4122
4123 Roo.bootstrap.TableBody = function(config){
4124     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4125 };
4126
4127 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4128     
4129     cls: false,
4130     tag: false,
4131     align: false,
4132     charoff: false,
4133     valign: false,
4134     
4135     getAutoCreate : function(){
4136         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4137         
4138         cfg = {
4139             tag: 'tbody'
4140         }
4141             
4142         if (this.cls) {
4143             cfg.cls=this.cls
4144         }
4145         if(this.tag){
4146             cfg.tag = this.tag;
4147         }
4148         
4149         if(this.align){
4150             cfg.align = this.align;
4151         }
4152         if(this.charoff){
4153             cfg.charoff = this.charoff;
4154         }
4155         if(this.valign){
4156             cfg.valign = this.valign;
4157         }
4158         
4159         return cfg;
4160     }
4161     
4162     
4163 //    initEvents : function()
4164 //    {
4165 //        
4166 //        if(!this.store){
4167 //            return;
4168 //        }
4169 //        
4170 //        this.store = Roo.factory(this.store, Roo.data);
4171 //        this.store.on('load', this.onLoad, this);
4172 //        
4173 //        this.store.load();
4174 //        
4175 //    },
4176 //    
4177 //    onLoad: function () 
4178 //    {   
4179 //        this.fireEvent('load', this);
4180 //    }
4181 //    
4182 //   
4183 });
4184
4185  
4186
4187  /*
4188  * Based on:
4189  * Ext JS Library 1.1.1
4190  * Copyright(c) 2006-2007, Ext JS, LLC.
4191  *
4192  * Originally Released Under LGPL - original licence link has changed is not relivant.
4193  *
4194  * Fork - LGPL
4195  * <script type="text/javascript">
4196  */
4197
4198 // as we use this in bootstrap.
4199 Roo.namespace('Roo.form');
4200  /**
4201  * @class Roo.form.Action
4202  * Internal Class used to handle form actions
4203  * @constructor
4204  * @param {Roo.form.BasicForm} el The form element or its id
4205  * @param {Object} config Configuration options
4206  */
4207
4208  
4209  
4210 // define the action interface
4211 Roo.form.Action = function(form, options){
4212     this.form = form;
4213     this.options = options || {};
4214 };
4215 /**
4216  * Client Validation Failed
4217  * @const 
4218  */
4219 Roo.form.Action.CLIENT_INVALID = 'client';
4220 /**
4221  * Server Validation Failed
4222  * @const 
4223  */
4224 Roo.form.Action.SERVER_INVALID = 'server';
4225  /**
4226  * Connect to Server Failed
4227  * @const 
4228  */
4229 Roo.form.Action.CONNECT_FAILURE = 'connect';
4230 /**
4231  * Reading Data from Server Failed
4232  * @const 
4233  */
4234 Roo.form.Action.LOAD_FAILURE = 'load';
4235
4236 Roo.form.Action.prototype = {
4237     type : 'default',
4238     failureType : undefined,
4239     response : undefined,
4240     result : undefined,
4241
4242     // interface method
4243     run : function(options){
4244
4245     },
4246
4247     // interface method
4248     success : function(response){
4249
4250     },
4251
4252     // interface method
4253     handleResponse : function(response){
4254
4255     },
4256
4257     // default connection failure
4258     failure : function(response){
4259         
4260         this.response = response;
4261         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4262         this.form.afterAction(this, false);
4263     },
4264
4265     processResponse : function(response){
4266         this.response = response;
4267         if(!response.responseText){
4268             return true;
4269         }
4270         this.result = this.handleResponse(response);
4271         return this.result;
4272     },
4273
4274     // utility functions used internally
4275     getUrl : function(appendParams){
4276         var url = this.options.url || this.form.url || this.form.el.dom.action;
4277         if(appendParams){
4278             var p = this.getParams();
4279             if(p){
4280                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4281             }
4282         }
4283         return url;
4284     },
4285
4286     getMethod : function(){
4287         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4288     },
4289
4290     getParams : function(){
4291         var bp = this.form.baseParams;
4292         var p = this.options.params;
4293         if(p){
4294             if(typeof p == "object"){
4295                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4296             }else if(typeof p == 'string' && bp){
4297                 p += '&' + Roo.urlEncode(bp);
4298             }
4299         }else if(bp){
4300             p = Roo.urlEncode(bp);
4301         }
4302         return p;
4303     },
4304
4305     createCallback : function(){
4306         return {
4307             success: this.success,
4308             failure: this.failure,
4309             scope: this,
4310             timeout: (this.form.timeout*1000),
4311             upload: this.form.fileUpload ? this.success : undefined
4312         };
4313     }
4314 };
4315
4316 Roo.form.Action.Submit = function(form, options){
4317     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4318 };
4319
4320 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4321     type : 'submit',
4322
4323     haveProgress : false,
4324     uploadComplete : false,
4325     
4326     // uploadProgress indicator.
4327     uploadProgress : function()
4328     {
4329         if (!this.form.progressUrl) {
4330             return;
4331         }
4332         
4333         if (!this.haveProgress) {
4334             Roo.MessageBox.progress("Uploading", "Uploading");
4335         }
4336         if (this.uploadComplete) {
4337            Roo.MessageBox.hide();
4338            return;
4339         }
4340         
4341         this.haveProgress = true;
4342    
4343         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4344         
4345         var c = new Roo.data.Connection();
4346         c.request({
4347             url : this.form.progressUrl,
4348             params: {
4349                 id : uid
4350             },
4351             method: 'GET',
4352             success : function(req){
4353                //console.log(data);
4354                 var rdata = false;
4355                 var edata;
4356                 try  {
4357                    rdata = Roo.decode(req.responseText)
4358                 } catch (e) {
4359                     Roo.log("Invalid data from server..");
4360                     Roo.log(edata);
4361                     return;
4362                 }
4363                 if (!rdata || !rdata.success) {
4364                     Roo.log(rdata);
4365                     Roo.MessageBox.alert(Roo.encode(rdata));
4366                     return;
4367                 }
4368                 var data = rdata.data;
4369                 
4370                 if (this.uploadComplete) {
4371                    Roo.MessageBox.hide();
4372                    return;
4373                 }
4374                    
4375                 if (data){
4376                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4377                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4378                     );
4379                 }
4380                 this.uploadProgress.defer(2000,this);
4381             },
4382        
4383             failure: function(data) {
4384                 Roo.log('progress url failed ');
4385                 Roo.log(data);
4386             },
4387             scope : this
4388         });
4389            
4390     },
4391     
4392     
4393     run : function()
4394     {
4395         // run get Values on the form, so it syncs any secondary forms.
4396         this.form.getValues();
4397         
4398         var o = this.options;
4399         var method = this.getMethod();
4400         var isPost = method == 'POST';
4401         if(o.clientValidation === false || this.form.isValid()){
4402             
4403             if (this.form.progressUrl) {
4404                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4405                     (new Date() * 1) + '' + Math.random());
4406                     
4407             } 
4408             
4409             
4410             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4411                 form:this.form.el.dom,
4412                 url:this.getUrl(!isPost),
4413                 method: method,
4414                 params:isPost ? this.getParams() : null,
4415                 isUpload: this.form.fileUpload
4416             }));
4417             
4418             this.uploadProgress();
4419
4420         }else if (o.clientValidation !== false){ // client validation failed
4421             this.failureType = Roo.form.Action.CLIENT_INVALID;
4422             this.form.afterAction(this, false);
4423         }
4424     },
4425
4426     success : function(response)
4427     {
4428         this.uploadComplete= true;
4429         if (this.haveProgress) {
4430             Roo.MessageBox.hide();
4431         }
4432         
4433         
4434         var result = this.processResponse(response);
4435         if(result === true || result.success){
4436             this.form.afterAction(this, true);
4437             return;
4438         }
4439         if(result.errors){
4440             this.form.markInvalid(result.errors);
4441             this.failureType = Roo.form.Action.SERVER_INVALID;
4442         }
4443         this.form.afterAction(this, false);
4444     },
4445     failure : function(response)
4446     {
4447         this.uploadComplete= true;
4448         if (this.haveProgress) {
4449             Roo.MessageBox.hide();
4450         }
4451         
4452         this.response = response;
4453         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4454         this.form.afterAction(this, false);
4455     },
4456     
4457     handleResponse : function(response){
4458         if(this.form.errorReader){
4459             var rs = this.form.errorReader.read(response);
4460             var errors = [];
4461             if(rs.records){
4462                 for(var i = 0, len = rs.records.length; i < len; i++) {
4463                     var r = rs.records[i];
4464                     errors[i] = r.data;
4465                 }
4466             }
4467             if(errors.length < 1){
4468                 errors = null;
4469             }
4470             return {
4471                 success : rs.success,
4472                 errors : errors
4473             };
4474         }
4475         var ret = false;
4476         try {
4477             ret = Roo.decode(response.responseText);
4478         } catch (e) {
4479             ret = {
4480                 success: false,
4481                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4482                 errors : []
4483             };
4484         }
4485         return ret;
4486         
4487     }
4488 });
4489
4490
4491 Roo.form.Action.Load = function(form, options){
4492     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4493     this.reader = this.form.reader;
4494 };
4495
4496 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4497     type : 'load',
4498
4499     run : function(){
4500         
4501         Roo.Ajax.request(Roo.apply(
4502                 this.createCallback(), {
4503                     method:this.getMethod(),
4504                     url:this.getUrl(false),
4505                     params:this.getParams()
4506         }));
4507     },
4508
4509     success : function(response){
4510         
4511         var result = this.processResponse(response);
4512         if(result === true || !result.success || !result.data){
4513             this.failureType = Roo.form.Action.LOAD_FAILURE;
4514             this.form.afterAction(this, false);
4515             return;
4516         }
4517         this.form.clearInvalid();
4518         this.form.setValues(result.data);
4519         this.form.afterAction(this, true);
4520     },
4521
4522     handleResponse : function(response){
4523         if(this.form.reader){
4524             var rs = this.form.reader.read(response);
4525             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4526             return {
4527                 success : rs.success,
4528                 data : data
4529             };
4530         }
4531         return Roo.decode(response.responseText);
4532     }
4533 });
4534
4535 Roo.form.Action.ACTION_TYPES = {
4536     'load' : Roo.form.Action.Load,
4537     'submit' : Roo.form.Action.Submit
4538 };/*
4539  * - LGPL
4540  *
4541  * form
4542  * 
4543  */
4544
4545 /**
4546  * @class Roo.bootstrap.Form
4547  * @extends Roo.bootstrap.Component
4548  * Bootstrap Form class
4549  * @cfg {String} method  GET | POST (default POST)
4550  * @cfg {String} labelAlign top | left (default top)
4551   * @cfg {String} align left  | right - for navbars
4552
4553  * 
4554  * @constructor
4555  * Create a new Form
4556  * @param {Object} config The config object
4557  */
4558
4559
4560 Roo.bootstrap.Form = function(config){
4561     Roo.bootstrap.Form.superclass.constructor.call(this, config);
4562     this.addEvents({
4563         /**
4564          * @event clientvalidation
4565          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4566          * @param {Form} this
4567          * @param {Boolean} valid true if the form has passed client-side validation
4568          */
4569         clientvalidation: true,
4570         /**
4571          * @event beforeaction
4572          * Fires before any action is performed. Return false to cancel the action.
4573          * @param {Form} this
4574          * @param {Action} action The action to be performed
4575          */
4576         beforeaction: true,
4577         /**
4578          * @event actionfailed
4579          * Fires when an action fails.
4580          * @param {Form} this
4581          * @param {Action} action The action that failed
4582          */
4583         actionfailed : true,
4584         /**
4585          * @event actioncomplete
4586          * Fires when an action is completed.
4587          * @param {Form} this
4588          * @param {Action} action The action that completed
4589          */
4590         actioncomplete : true
4591     });
4592     
4593 };
4594
4595 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
4596       
4597      /**
4598      * @cfg {String} method
4599      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4600      */
4601     method : 'POST',
4602     /**
4603      * @cfg {String} url
4604      * The URL to use for form actions if one isn't supplied in the action options.
4605      */
4606     /**
4607      * @cfg {Boolean} fileUpload
4608      * Set to true if this form is a file upload.
4609      */
4610      
4611     /**
4612      * @cfg {Object} baseParams
4613      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4614      */
4615       
4616     /**
4617      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4618      */
4619     timeout: 30,
4620     /**
4621      * @cfg {Sting} align (left|right) for navbar forms
4622      */
4623     align : 'left',
4624
4625     // private
4626     activeAction : null,
4627  
4628     /**
4629      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4630      * element by passing it or its id or mask the form itself by passing in true.
4631      * @type Mixed
4632      */
4633     waitMsgTarget : false,
4634     
4635      
4636     
4637     /**
4638      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4639      * element by passing it or its id or mask the form itself by passing in true.
4640      * @type Mixed
4641      */
4642     
4643     getAutoCreate : function(){
4644         
4645         var cfg = {
4646             tag: 'form',
4647             method : this.method || 'POST',
4648             id : this.id || Roo.id(),
4649             cls : ''
4650         }
4651         if (this.parent().xtype.match(/^Nav/)) {
4652             cfg.cls = 'navbar-form navbar-' + this.align;
4653             
4654         }
4655         
4656         if (this.labelAlign == 'left' ) {
4657             cfg.cls += ' form-horizontal';
4658         }
4659         
4660         
4661         return cfg;
4662     },
4663     initEvents : function()
4664     {
4665         this.el.on('submit', this.onSubmit, this);
4666         
4667         
4668     },
4669     // private
4670     onSubmit : function(e){
4671         e.stopEvent();
4672     },
4673     
4674      /**
4675      * Returns true if client-side validation on the form is successful.
4676      * @return Boolean
4677      */
4678     isValid : function(){
4679         var items = this.getItems();
4680         var valid = true;
4681         items.each(function(f){
4682            if(!f.validate()){
4683                valid = false;
4684                
4685            }
4686         });
4687         return valid;
4688     },
4689     /**
4690      * Returns true if any fields in this form have changed since their original load.
4691      * @return Boolean
4692      */
4693     isDirty : function(){
4694         var dirty = false;
4695         var items = this.getItems();
4696         items.each(function(f){
4697            if(f.isDirty()){
4698                dirty = true;
4699                return false;
4700            }
4701            return true;
4702         });
4703         return dirty;
4704     },
4705      /**
4706      * Performs a predefined action (submit or load) or custom actions you define on this form.
4707      * @param {String} actionName The name of the action type
4708      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
4709      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4710      * accept other config options):
4711      * <pre>
4712 Property          Type             Description
4713 ----------------  ---------------  ----------------------------------------------------------------------------------
4714 url               String           The url for the action (defaults to the form's url)
4715 method            String           The form method to use (defaults to the form's method, or POST if not defined)
4716 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
4717 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
4718                                    validate the form on the client (defaults to false)
4719      * </pre>
4720      * @return {BasicForm} this
4721      */
4722     doAction : function(action, options){
4723         if(typeof action == 'string'){
4724             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4725         }
4726         if(this.fireEvent('beforeaction', this, action) !== false){
4727             this.beforeAction(action);
4728             action.run.defer(100, action);
4729         }
4730         return this;
4731     },
4732     
4733     // private
4734     beforeAction : function(action){
4735         var o = action.options;
4736         
4737         // not really supported yet.. ??
4738         
4739         //if(this.waitMsgTarget === true){
4740             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4741         //}else if(this.waitMsgTarget){
4742         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4743         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4744         //}else {
4745         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4746        // }
4747          
4748     },
4749
4750     // private
4751     afterAction : function(action, success){
4752         this.activeAction = null;
4753         var o = action.options;
4754         
4755         //if(this.waitMsgTarget === true){
4756             this.el.unmask();
4757         //}else if(this.waitMsgTarget){
4758         //    this.waitMsgTarget.unmask();
4759         //}else{
4760         //    Roo.MessageBox.updateProgress(1);
4761         //    Roo.MessageBox.hide();
4762        // }
4763         // 
4764         if(success){
4765             if(o.reset){
4766                 this.reset();
4767             }
4768             Roo.callback(o.success, o.scope, [this, action]);
4769             this.fireEvent('actioncomplete', this, action);
4770             
4771         }else{
4772             
4773             // failure condition..
4774             // we have a scenario where updates need confirming.
4775             // eg. if a locking scenario exists..
4776             // we look for { errors : { needs_confirm : true }} in the response.
4777             if (
4778                 (typeof(action.result) != 'undefined')  &&
4779                 (typeof(action.result.errors) != 'undefined')  &&
4780                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4781            ){
4782                 var _t = this;
4783                 Roo.log("not supported yet");
4784                  /*
4785                 
4786                 Roo.MessageBox.confirm(
4787                     "Change requires confirmation",
4788                     action.result.errorMsg,
4789                     function(r) {
4790                         if (r != 'yes') {
4791                             return;
4792                         }
4793                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4794                     }
4795                     
4796                 );
4797                 */
4798                 
4799                 
4800                 return;
4801             }
4802             
4803             Roo.callback(o.failure, o.scope, [this, action]);
4804             // show an error message if no failed handler is set..
4805             if (!this.hasListener('actionfailed')) {
4806                 Roo.log("need to add dialog support");
4807                 /*
4808                 Roo.MessageBox.alert("Error",
4809                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4810                         action.result.errorMsg :
4811                         "Saving Failed, please check your entries or try again"
4812                 );
4813                 */
4814             }
4815             
4816             this.fireEvent('actionfailed', this, action);
4817         }
4818         
4819     },
4820     /**
4821      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4822      * @param {String} id The value to search for
4823      * @return Field
4824      */
4825     findField : function(id){
4826         var items = this.getItems();
4827         var field = items.get(id);
4828         if(!field){
4829              items.each(function(f){
4830                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4831                     field = f;
4832                     return false;
4833                 }
4834                 return true;
4835             });
4836         }
4837         return field || null;
4838     },
4839      /**
4840      * Mark fields in this form invalid in bulk.
4841      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4842      * @return {BasicForm} this
4843      */
4844     markInvalid : function(errors){
4845         if(errors instanceof Array){
4846             for(var i = 0, len = errors.length; i < len; i++){
4847                 var fieldError = errors[i];
4848                 var f = this.findField(fieldError.id);
4849                 if(f){
4850                     f.markInvalid(fieldError.msg);
4851                 }
4852             }
4853         }else{
4854             var field, id;
4855             for(id in errors){
4856                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4857                     field.markInvalid(errors[id]);
4858                 }
4859             }
4860         }
4861         //Roo.each(this.childForms || [], function (f) {
4862         //    f.markInvalid(errors);
4863         //});
4864         
4865         return this;
4866     },
4867
4868     /**
4869      * Set values for fields in this form in bulk.
4870      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4871      * @return {BasicForm} this
4872      */
4873     setValues : function(values){
4874         if(values instanceof Array){ // array of objects
4875             for(var i = 0, len = values.length; i < len; i++){
4876                 var v = values[i];
4877                 var f = this.findField(v.id);
4878                 if(f){
4879                     f.setValue(v.value);
4880                     if(this.trackResetOnLoad){
4881                         f.originalValue = f.getValue();
4882                     }
4883                 }
4884             }
4885         }else{ // object hash
4886             var field, id;
4887             for(id in values){
4888                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4889                     
4890                     if (field.setFromData && 
4891                         field.valueField && 
4892                         field.displayField &&
4893                         // combos' with local stores can 
4894                         // be queried via setValue()
4895                         // to set their value..
4896                         (field.store && !field.store.isLocal)
4897                         ) {
4898                         // it's a combo
4899                         var sd = { };
4900                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4901                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4902                         field.setFromData(sd);
4903                         
4904                     } else {
4905                         field.setValue(values[id]);
4906                     }
4907                     
4908                     
4909                     if(this.trackResetOnLoad){
4910                         field.originalValue = field.getValue();
4911                     }
4912                 }
4913             }
4914         }
4915          
4916         //Roo.each(this.childForms || [], function (f) {
4917         //    f.setValues(values);
4918         //});
4919                 
4920         return this;
4921     },
4922
4923     /**
4924      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4925      * they are returned as an array.
4926      * @param {Boolean} asString
4927      * @return {Object}
4928      */
4929     getValues : function(asString){
4930         //if (this.childForms) {
4931             // copy values from the child forms
4932         //    Roo.each(this.childForms, function (f) {
4933         //        this.setValues(f.getValues());
4934         //    }, this);
4935         //}
4936         
4937         
4938         
4939         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4940         if(asString === true){
4941             return fs;
4942         }
4943         return Roo.urlDecode(fs);
4944     },
4945     
4946     /**
4947      * Returns the fields in this form as an object with key/value pairs. 
4948      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4949      * @return {Object}
4950      */
4951     getFieldValues : function(with_hidden)
4952     {
4953         var items = this.getItems();
4954         var ret = {};
4955         items.each(function(f){
4956             if (!f.getName()) {
4957                 return;
4958             }
4959             var v = f.getValue();
4960             if (f.inputType =='radio') {
4961                 if (typeof(ret[f.getName()]) == 'undefined') {
4962                     ret[f.getName()] = ''; // empty..
4963                 }
4964                 
4965                 if (!f.el.dom.checked) {
4966                     return;
4967                     
4968                 }
4969                 v = f.el.dom.value;
4970                 
4971             }
4972             
4973             // not sure if this supported any more..
4974             if ((typeof(v) == 'object') && f.getRawValue) {
4975                 v = f.getRawValue() ; // dates..
4976             }
4977             // combo boxes where name != hiddenName...
4978             if (f.name != f.getName()) {
4979                 ret[f.name] = f.getRawValue();
4980             }
4981             ret[f.getName()] = v;
4982         });
4983         
4984         return ret;
4985     },
4986
4987     /**
4988      * Clears all invalid messages in this form.
4989      * @return {BasicForm} this
4990      */
4991     clearInvalid : function(){
4992         var items = this.getItems();
4993         
4994         items.each(function(f){
4995            f.clearInvalid();
4996         });
4997         
4998         
4999         
5000         return this;
5001     },
5002
5003     /**
5004      * Resets this form.
5005      * @return {BasicForm} this
5006      */
5007     reset : function(){
5008         var items = this.getItems();
5009         items.each(function(f){
5010             f.reset();
5011         });
5012         
5013         Roo.each(this.childForms || [], function (f) {
5014             f.reset();
5015         });
5016        
5017         
5018         return this;
5019     },
5020     getItems : function()
5021     {
5022         var r=new Roo.util.MixedCollection(false, function(o){
5023             return o.id || (o.id = Roo.id());
5024         });
5025         var iter = function(el) {
5026             if (el.inputEl) {
5027                 r.add(el);
5028             }
5029             if (!el.items) {
5030                 return;
5031             }
5032             Roo.each(el.items,function(e) {
5033                 iter(e);
5034             });
5035             
5036             
5037         };
5038         iter(this);
5039         return r;
5040         
5041         
5042         
5043         
5044     }
5045     
5046 });
5047
5048  
5049 /*
5050  * Based on:
5051  * Ext JS Library 1.1.1
5052  * Copyright(c) 2006-2007, Ext JS, LLC.
5053  *
5054  * Originally Released Under LGPL - original licence link has changed is not relivant.
5055  *
5056  * Fork - LGPL
5057  * <script type="text/javascript">
5058  */
5059 /**
5060  * @class Roo.form.VTypes
5061  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5062  * @singleton
5063  */
5064 Roo.form.VTypes = function(){
5065     // closure these in so they are only created once.
5066     var alpha = /^[a-zA-Z_]+$/;
5067     var alphanum = /^[a-zA-Z0-9_]+$/;
5068     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5069     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5070
5071     // All these messages and functions are configurable
5072     return {
5073         /**
5074          * The function used to validate email addresses
5075          * @param {String} value The email address
5076          */
5077         'email' : function(v){
5078             return email.test(v);
5079         },
5080         /**
5081          * The error text to display when the email validation function returns false
5082          * @type String
5083          */
5084         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5085         /**
5086          * The keystroke filter mask to be applied on email input
5087          * @type RegExp
5088          */
5089         'emailMask' : /[a-z0-9_\.\-@]/i,
5090
5091         /**
5092          * The function used to validate URLs
5093          * @param {String} value The URL
5094          */
5095         'url' : function(v){
5096             return url.test(v);
5097         },
5098         /**
5099          * The error text to display when the url validation function returns false
5100          * @type String
5101          */
5102         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5103         
5104         /**
5105          * The function used to validate alpha values
5106          * @param {String} value The value
5107          */
5108         'alpha' : function(v){
5109             return alpha.test(v);
5110         },
5111         /**
5112          * The error text to display when the alpha validation function returns false
5113          * @type String
5114          */
5115         'alphaText' : 'This field should only contain letters and _',
5116         /**
5117          * The keystroke filter mask to be applied on alpha input
5118          * @type RegExp
5119          */
5120         'alphaMask' : /[a-z_]/i,
5121
5122         /**
5123          * The function used to validate alphanumeric values
5124          * @param {String} value The value
5125          */
5126         'alphanum' : function(v){
5127             return alphanum.test(v);
5128         },
5129         /**
5130          * The error text to display when the alphanumeric validation function returns false
5131          * @type String
5132          */
5133         'alphanumText' : 'This field should only contain letters, numbers and _',
5134         /**
5135          * The keystroke filter mask to be applied on alphanumeric input
5136          * @type RegExp
5137          */
5138         'alphanumMask' : /[a-z0-9_]/i
5139     };
5140 }();/*
5141  * - LGPL
5142  *
5143  * Input
5144  * 
5145  */
5146
5147 /**
5148  * @class Roo.bootstrap.Input
5149  * @extends Roo.bootstrap.Component
5150  * Bootstrap Input class
5151  * @cfg {Boolean} disabled is it disabled
5152  * @cfg {String} fieldLabel - the label associated
5153  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5154  * @cfg {String} name name of the input
5155  * @cfg {string} fieldLabel - the label associated
5156  * @cfg {string}  inputType - input / file submit ...
5157  * @cfg {string} placeholder - placeholder to put in text.
5158  * @cfg {string}  before - input group add on before
5159  * @cfg {string} after - input group add on after
5160  * @cfg {string} size - (lg|sm) or leave empty..
5161  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5162  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5163  * @cfg {Number} md colspan out of 12 for computer-sized screens
5164  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5165  * @cfg {string} value default value of the input
5166  * @cfg {Number} labelWidth set the width of label (0-12)
5167  * @cfg {String} labelAlign (top|left)
5168  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5169  * 
5170  * 
5171  * @constructor
5172  * Create a new Input
5173  * @param {Object} config The config object
5174  */
5175
5176 Roo.bootstrap.Input = function(config){
5177     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5178    
5179         this.addEvents({
5180             /**
5181              * @event focus
5182              * Fires when this field receives input focus.
5183              * @param {Roo.form.Field} this
5184              */
5185             focus : true,
5186             /**
5187              * @event blur
5188              * Fires when this field loses input focus.
5189              * @param {Roo.form.Field} this
5190              */
5191             blur : true,
5192             /**
5193              * @event specialkey
5194              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5195              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5196              * @param {Roo.form.Field} this
5197              * @param {Roo.EventObject} e The event object
5198              */
5199             specialkey : true,
5200             /**
5201              * @event change
5202              * Fires just before the field blurs if the field value has changed.
5203              * @param {Roo.form.Field} this
5204              * @param {Mixed} newValue The new value
5205              * @param {Mixed} oldValue The original value
5206              */
5207             change : true,
5208             /**
5209              * @event invalid
5210              * Fires after the field has been marked as invalid.
5211              * @param {Roo.form.Field} this
5212              * @param {String} msg The validation message
5213              */
5214             invalid : true,
5215             /**
5216              * @event valid
5217              * Fires after the field has been validated with no errors.
5218              * @param {Roo.form.Field} this
5219              */
5220             valid : true,
5221              /**
5222              * @event keyup
5223              * Fires after the key up
5224              * @param {Roo.form.Field} this
5225              * @param {Roo.EventObject}  e The event Object
5226              */
5227             keyup : true
5228         });
5229 };
5230
5231 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5232      /**
5233      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5234       automatic validation (defaults to "keyup").
5235      */
5236     validationEvent : "keyup",
5237      /**
5238      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5239      */
5240     validateOnBlur : true,
5241     /**
5242      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5243      */
5244     validationDelay : 250,
5245      /**
5246      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5247      */
5248     focusClass : "x-form-focus",  // not needed???
5249     
5250        
5251     /**
5252      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5253      */
5254     invalidClass : "has-error",
5255     
5256     /**
5257      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5258      */
5259     selectOnFocus : false,
5260     
5261      /**
5262      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5263      */
5264     maskRe : null,
5265        /**
5266      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5267      */
5268     vtype : null,
5269     
5270       /**
5271      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5272      */
5273     disableKeyFilter : false,
5274     
5275        /**
5276      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5277      */
5278     disabled : false,
5279      /**
5280      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5281      */
5282     allowBlank : true,
5283     /**
5284      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5285      */
5286     blankText : "This field is required",
5287     
5288      /**
5289      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5290      */
5291     minLength : 0,
5292     /**
5293      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5294      */
5295     maxLength : Number.MAX_VALUE,
5296     /**
5297      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5298      */
5299     minLengthText : "The minimum length for this field is {0}",
5300     /**
5301      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5302      */
5303     maxLengthText : "The maximum length for this field is {0}",
5304   
5305     
5306     /**
5307      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5308      * If available, this function will be called only after the basic validators all return true, and will be passed the
5309      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5310      */
5311     validator : null,
5312     /**
5313      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5314      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5315      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5316      */
5317     regex : null,
5318     /**
5319      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5320      */
5321     regexText : "",
5322     
5323     
5324     
5325     fieldLabel : '',
5326     inputType : 'text',
5327     
5328     name : false,
5329     placeholder: false,
5330     before : false,
5331     after : false,
5332     size : false,
5333     // private
5334     hasFocus : false,
5335     preventMark: false,
5336     isFormField : true,
5337     value : '',
5338     labelWidth : 2,
5339     labelAlign : false,
5340     readOnly : false,
5341     
5342     parentLabelAlign : function()
5343     {
5344         var parent = this;
5345         while (parent.parent()) {
5346             parent = parent.parent();
5347             if (typeof(parent.labelAlign) !='undefined') {
5348                 return parent.labelAlign;
5349             }
5350         }
5351         return 'left';
5352         
5353     },
5354     
5355     getAutoCreate : function(){
5356         
5357         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5358         
5359         var id = Roo.id();
5360         
5361         var cfg = {};
5362         
5363         if(this.inputType != 'hidden'){
5364             cfg.cls = 'form-group' //input-group
5365         }
5366         
5367         var input =  {
5368             tag: 'input',
5369             id : id,
5370             type : this.inputType,
5371             value : this.value,
5372             cls : 'form-control',
5373             placeholder : this.placeholder || ''
5374             
5375         };
5376         
5377         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5378             input.maxLength = this.maxLength;
5379         }
5380         
5381         if (this.disabled) {
5382             input.disabled=true;
5383         }
5384         
5385         if (this.readOnly) {
5386             input.readonly=true;
5387         }
5388         
5389         if (this.name) {
5390             input.name = this.name;
5391         }
5392         if (this.size) {
5393             input.cls += ' input-' + this.size;
5394         }
5395         var settings=this;
5396         ['xs','sm','md','lg'].map(function(size){
5397             if (settings[size]) {
5398                 cfg.cls += ' col-' + size + '-' + settings[size];
5399             }
5400         });
5401         
5402         var inputblock = input;
5403         
5404         if (this.before || this.after) {
5405             
5406             inputblock = {
5407                 cls : 'input-group',
5408                 cn :  [] 
5409             };
5410             if (this.before) {
5411                 inputblock.cn.push({
5412                     tag :'span',
5413                     cls : 'input-group-addon',
5414                     html : this.before
5415                 });
5416             }
5417             inputblock.cn.push(input);
5418             if (this.after) {
5419                 inputblock.cn.push({
5420                     tag :'span',
5421                     cls : 'input-group-addon',
5422                     html : this.after
5423                 });
5424             }
5425             
5426         };
5427         
5428         if (align ==='left' && this.fieldLabel.length) {
5429                 Roo.log("left and has label");
5430                 cfg.cn = [
5431                     
5432                     {
5433                         tag: 'label',
5434                         'for' :  id,
5435                         cls : 'control-label col-sm-' + this.labelWidth,
5436                         html : this.fieldLabel
5437                         
5438                     },
5439                     {
5440                         cls : "col-sm-" + (12 - this.labelWidth), 
5441                         cn: [
5442                             inputblock
5443                         ]
5444                     }
5445                     
5446                 ];
5447         } else if ( this.fieldLabel.length) {
5448                 Roo.log(" label");
5449                  cfg.cn = [
5450                    
5451                     {
5452                         tag: 'label',
5453                         //cls : 'input-group-addon',
5454                         html : this.fieldLabel
5455                         
5456                     },
5457                     
5458                     inputblock
5459                     
5460                 ];
5461
5462         } else {
5463             
5464                 Roo.log(" no label && no align");
5465                 cfg.cn = [
5466                     
5467                         inputblock
5468                     
5469                 ];
5470                 
5471                 
5472         };
5473         Roo.log('input-parentType: ' + this.parentType);
5474         
5475         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5476            cfg.cls += ' navbar-form';
5477            Roo.log(cfg);
5478         }
5479         
5480         return cfg;
5481         
5482     },
5483     /**
5484      * return the real input element.
5485      */
5486     inputEl: function ()
5487     {
5488         return this.el.select('input.form-control',true).first();
5489     },
5490     setDisabled : function(v)
5491     {
5492         var i  = this.inputEl().dom;
5493         if (!v) {
5494             i.removeAttribute('disabled');
5495             return;
5496             
5497         }
5498         i.setAttribute('disabled','true');
5499     },
5500     initEvents : function()
5501     {
5502         
5503         this.inputEl().on("keydown" , this.fireKey,  this);
5504         this.inputEl().on("focus", this.onFocus,  this);
5505         this.inputEl().on("blur", this.onBlur,  this);
5506         
5507         this.inputEl().relayEvent('keyup', this);
5508
5509         // reference to original value for reset
5510         this.originalValue = this.getValue();
5511         //Roo.form.TextField.superclass.initEvents.call(this);
5512         if(this.validationEvent == 'keyup'){
5513             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5514             this.inputEl().on('keyup', this.filterValidation, this);
5515         }
5516         else if(this.validationEvent !== false){
5517             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5518         }
5519         
5520         if(this.selectOnFocus){
5521             this.on("focus", this.preFocus, this);
5522             
5523         }
5524         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5525             this.inputEl().on("keypress", this.filterKeys, this);
5526         }
5527        /* if(this.grow){
5528             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
5529             this.el.on("click", this.autoSize,  this);
5530         }
5531         */
5532         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5533             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5534         }
5535         
5536     },
5537     filterValidation : function(e){
5538         if(!e.isNavKeyPress()){
5539             this.validationTask.delay(this.validationDelay);
5540         }
5541     },
5542      /**
5543      * Validates the field value
5544      * @return {Boolean} True if the value is valid, else false
5545      */
5546     validate : function(){
5547         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5548         if(this.disabled || this.validateValue(this.getRawValue())){
5549             this.clearInvalid();
5550             return true;
5551         }
5552         return false;
5553     },
5554     
5555     
5556     /**
5557      * Validates a value according to the field's validation rules and marks the field as invalid
5558      * if the validation fails
5559      * @param {Mixed} value The value to validate
5560      * @return {Boolean} True if the value is valid, else false
5561      */
5562     validateValue : function(value){
5563         if(value.length < 1)  { // if it's blank
5564              if(this.allowBlank){
5565                 this.clearInvalid();
5566                 return true;
5567              }else{
5568                 this.markInvalid(this.blankText);
5569                 return false;
5570              }
5571         }
5572         if(value.length < this.minLength){
5573             this.markInvalid(String.format(this.minLengthText, this.minLength));
5574             return false;
5575         }
5576         if(value.length > this.maxLength){
5577             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5578             return false;
5579         }
5580         if(this.vtype){
5581             var vt = Roo.form.VTypes;
5582             if(!vt[this.vtype](value, this)){
5583                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5584                 return false;
5585             }
5586         }
5587         if(typeof this.validator == "function"){
5588             var msg = this.validator(value);
5589             if(msg !== true){
5590                 this.markInvalid(msg);
5591                 return false;
5592             }
5593         }
5594         if(this.regex && !this.regex.test(value)){
5595             this.markInvalid(this.regexText);
5596             return false;
5597         }
5598         return true;
5599     },
5600
5601     
5602     
5603      // private
5604     fireKey : function(e){
5605         //Roo.log('field ' + e.getKey());
5606         if(e.isNavKeyPress()){
5607             this.fireEvent("specialkey", this, e);
5608         }
5609     },
5610     focus : function (selectText){
5611         if(this.rendered){
5612             this.inputEl().focus();
5613             if(selectText === true){
5614                 this.inputEl().dom.select();
5615             }
5616         }
5617         return this;
5618     } ,
5619     
5620     onFocus : function(){
5621         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5622            // this.el.addClass(this.focusClass);
5623         }
5624         if(!this.hasFocus){
5625             this.hasFocus = true;
5626             this.startValue = this.getValue();
5627             this.fireEvent("focus", this);
5628         }
5629     },
5630     
5631     beforeBlur : Roo.emptyFn,
5632
5633     
5634     // private
5635     onBlur : function(){
5636         this.beforeBlur();
5637         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5638             //this.el.removeClass(this.focusClass);
5639         }
5640         this.hasFocus = false;
5641         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5642             this.validate();
5643         }
5644         var v = this.getValue();
5645         if(String(v) !== String(this.startValue)){
5646             this.fireEvent('change', this, v, this.startValue);
5647         }
5648         this.fireEvent("blur", this);
5649     },
5650     
5651     /**
5652      * Resets the current field value to the originally loaded value and clears any validation messages
5653      */
5654     reset : function(){
5655         this.setValue(this.originalValue);
5656         this.clearInvalid();
5657     },
5658      /**
5659      * Returns the name of the field
5660      * @return {Mixed} name The name field
5661      */
5662     getName: function(){
5663         return this.name;
5664     },
5665      /**
5666      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
5667      * @return {Mixed} value The field value
5668      */
5669     getValue : function(){
5670         return this.inputEl().getValue();
5671     },
5672     /**
5673      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
5674      * @return {Mixed} value The field value
5675      */
5676     getRawValue : function(){
5677         var v = this.inputEl().getValue();
5678         
5679         return v;
5680     },
5681     
5682     /**
5683      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
5684      * @param {Mixed} value The value to set
5685      */
5686     setRawValue : function(v){
5687         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5688     },
5689     
5690     selectText : function(start, end){
5691         var v = this.getRawValue();
5692         if(v.length > 0){
5693             start = start === undefined ? 0 : start;
5694             end = end === undefined ? v.length : end;
5695             var d = this.inputEl().dom;
5696             if(d.setSelectionRange){
5697                 d.setSelectionRange(start, end);
5698             }else if(d.createTextRange){
5699                 var range = d.createTextRange();
5700                 range.moveStart("character", start);
5701                 range.moveEnd("character", v.length-end);
5702                 range.select();
5703             }
5704         }
5705     },
5706     
5707     /**
5708      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
5709      * @param {Mixed} value The value to set
5710      */
5711     setValue : function(v){
5712         this.value = v;
5713         if(this.rendered){
5714             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5715             this.validate();
5716         }
5717     },
5718     
5719     /*
5720     processValue : function(value){
5721         if(this.stripCharsRe){
5722             var newValue = value.replace(this.stripCharsRe, '');
5723             if(newValue !== value){
5724                 this.setRawValue(newValue);
5725                 return newValue;
5726             }
5727         }
5728         return value;
5729     },
5730   */
5731     preFocus : function(){
5732         
5733         if(this.selectOnFocus){
5734             this.inputEl().dom.select();
5735         }
5736     },
5737     filterKeys : function(e){
5738         var k = e.getKey();
5739         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5740             return;
5741         }
5742         var c = e.getCharCode(), cc = String.fromCharCode(c);
5743         if(Roo.isIE && (e.isSpecialKey() || !cc)){
5744             return;
5745         }
5746         if(!this.maskRe.test(cc)){
5747             e.stopEvent();
5748         }
5749     },
5750      /**
5751      * Clear any invalid styles/messages for this field
5752      */
5753     clearInvalid : function(){
5754         
5755         if(!this.el || this.preventMark){ // not rendered
5756             return;
5757         }
5758         this.el.removeClass(this.invalidClass);
5759         /*
5760         switch(this.msgTarget){
5761             case 'qtip':
5762                 this.el.dom.qtip = '';
5763                 break;
5764             case 'title':
5765                 this.el.dom.title = '';
5766                 break;
5767             case 'under':
5768                 if(this.errorEl){
5769                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5770                 }
5771                 break;
5772             case 'side':
5773                 if(this.errorIcon){
5774                     this.errorIcon.dom.qtip = '';
5775                     this.errorIcon.hide();
5776                     this.un('resize', this.alignErrorIcon, this);
5777                 }
5778                 break;
5779             default:
5780                 var t = Roo.getDom(this.msgTarget);
5781                 t.innerHTML = '';
5782                 t.style.display = 'none';
5783                 break;
5784         }
5785         */
5786         this.fireEvent('valid', this);
5787     },
5788      /**
5789      * Mark this field as invalid
5790      * @param {String} msg The validation message
5791      */
5792     markInvalid : function(msg){
5793         if(!this.el  || this.preventMark){ // not rendered
5794             return;
5795         }
5796         this.el.addClass(this.invalidClass);
5797         /*
5798         msg = msg || this.invalidText;
5799         switch(this.msgTarget){
5800             case 'qtip':
5801                 this.el.dom.qtip = msg;
5802                 this.el.dom.qclass = 'x-form-invalid-tip';
5803                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5804                     Roo.QuickTips.enable();
5805                 }
5806                 break;
5807             case 'title':
5808                 this.el.dom.title = msg;
5809                 break;
5810             case 'under':
5811                 if(!this.errorEl){
5812                     var elp = this.el.findParent('.x-form-element', 5, true);
5813                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5814                     this.errorEl.setWidth(elp.getWidth(true)-20);
5815                 }
5816                 this.errorEl.update(msg);
5817                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5818                 break;
5819             case 'side':
5820                 if(!this.errorIcon){
5821                     var elp = this.el.findParent('.x-form-element', 5, true);
5822                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5823                 }
5824                 this.alignErrorIcon();
5825                 this.errorIcon.dom.qtip = msg;
5826                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5827                 this.errorIcon.show();
5828                 this.on('resize', this.alignErrorIcon, this);
5829                 break;
5830             default:
5831                 var t = Roo.getDom(this.msgTarget);
5832                 t.innerHTML = msg;
5833                 t.style.display = this.msgDisplay;
5834                 break;
5835         }
5836         */
5837         this.fireEvent('invalid', this, msg);
5838     },
5839     // private
5840     SafariOnKeyDown : function(event)
5841     {
5842         // this is a workaround for a password hang bug on chrome/ webkit.
5843         
5844         var isSelectAll = false;
5845         
5846         if(this.inputEl().dom.selectionEnd > 0){
5847             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5848         }
5849         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5850             event.preventDefault();
5851             this.setValue('');
5852             return;
5853         }
5854         
5855         if(isSelectAll){ // backspace and delete key
5856             
5857             event.preventDefault();
5858             // this is very hacky as keydown always get's upper case.
5859             //
5860             var cc = String.fromCharCode(event.getCharCode());
5861             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5862             
5863         }
5864     },
5865     adjustWidth : function(tag, w){
5866         tag = tag.toLowerCase();
5867         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5868             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5869                 if(tag == 'input'){
5870                     return w + 2;
5871                 }
5872                 if(tag == 'textarea'){
5873                     return w-2;
5874                 }
5875             }else if(Roo.isOpera){
5876                 if(tag == 'input'){
5877                     return w + 2;
5878                 }
5879                 if(tag == 'textarea'){
5880                     return w-2;
5881                 }
5882             }
5883         }
5884         return w;
5885     }
5886     
5887 });
5888
5889  
5890 /*
5891  * - LGPL
5892  *
5893  * Input
5894  * 
5895  */
5896
5897 /**
5898  * @class Roo.bootstrap.TextArea
5899  * @extends Roo.bootstrap.Input
5900  * Bootstrap TextArea class
5901  * @cfg {Number} cols Specifies the visible width of a text area
5902  * @cfg {Number} rows Specifies the visible number of lines in a text area
5903  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5904  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5905  * @cfg {string} html text
5906  * 
5907  * @constructor
5908  * Create a new TextArea
5909  * @param {Object} config The config object
5910  */
5911
5912 Roo.bootstrap.TextArea = function(config){
5913     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5914    
5915 };
5916
5917 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5918      
5919     cols : false,
5920     rows : 5,
5921     readOnly : false,
5922     warp : 'soft',
5923     resize : false,
5924     value: false,
5925     html: false,
5926     
5927     getAutoCreate : function(){
5928         
5929         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5930         
5931         var id = Roo.id();
5932         
5933         var cfg = {};
5934         
5935         var input =  {
5936             tag: 'textarea',
5937             id : id,
5938             warp : this.warp,
5939             rows : this.rows,
5940             value : this.value || '',
5941             html: this.html || '',
5942             cls : 'form-control',
5943             placeholder : this.placeholder || '' 
5944             
5945         };
5946         
5947         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5948             input.maxLength = this.maxLength;
5949         }
5950         
5951         if(this.resize){
5952             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5953         }
5954         
5955         if(this.cols){
5956             input.cols = this.cols;
5957         }
5958         
5959         if (this.readOnly) {
5960             input.readonly = true;
5961         }
5962         
5963         if (this.name) {
5964             input.name = this.name;
5965         }
5966         
5967         if (this.size) {
5968             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5969         }
5970         
5971         var settings=this;
5972         ['xs','sm','md','lg'].map(function(size){
5973             if (settings[size]) {
5974                 cfg.cls += ' col-' + size + '-' + settings[size];
5975             }
5976         });
5977         
5978         var inputblock = input;
5979         
5980         if (this.before || this.after) {
5981             
5982             inputblock = {
5983                 cls : 'input-group',
5984                 cn :  [] 
5985             };
5986             if (this.before) {
5987                 inputblock.cn.push({
5988                     tag :'span',
5989                     cls : 'input-group-addon',
5990                     html : this.before
5991                 });
5992             }
5993             inputblock.cn.push(input);
5994             if (this.after) {
5995                 inputblock.cn.push({
5996                     tag :'span',
5997                     cls : 'input-group-addon',
5998                     html : this.after
5999                 });
6000             }
6001             
6002         }
6003         
6004         if (align ==='left' && this.fieldLabel.length) {
6005                 Roo.log("left and has label");
6006                 cfg.cn = [
6007                     
6008                     {
6009                         tag: 'label',
6010                         'for' :  id,
6011                         cls : 'control-label col-sm-' + this.labelWidth,
6012                         html : this.fieldLabel
6013                         
6014                     },
6015                     {
6016                         cls : "col-sm-" + (12 - this.labelWidth), 
6017                         cn: [
6018                             inputblock
6019                         ]
6020                     }
6021                     
6022                 ];
6023         } else if ( this.fieldLabel.length) {
6024                 Roo.log(" label");
6025                  cfg.cn = [
6026                    
6027                     {
6028                         tag: 'label',
6029                         //cls : 'input-group-addon',
6030                         html : this.fieldLabel
6031                         
6032                     },
6033                     
6034                     inputblock
6035                     
6036                 ];
6037
6038         } else {
6039             
6040                    Roo.log(" no label && no align");
6041                 cfg.cn = [
6042                     
6043                         inputblock
6044                     
6045                 ];
6046                 
6047                 
6048         }
6049         
6050         if (this.disabled) {
6051             input.disabled=true;
6052         }
6053         
6054         return cfg;
6055         
6056     },
6057     /**
6058      * return the real textarea element.
6059      */
6060     inputEl: function ()
6061     {
6062         return this.el.select('textarea.form-control',true).first();
6063     }
6064 });
6065
6066  
6067 /*
6068  * - LGPL
6069  *
6070  * trigger field - base class for combo..
6071  * 
6072  */
6073  
6074 /**
6075  * @class Roo.bootstrap.TriggerField
6076  * @extends Roo.bootstrap.Input
6077  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6078  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6079  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6080  * for which you can provide a custom implementation.  For example:
6081  * <pre><code>
6082 var trigger = new Roo.bootstrap.TriggerField();
6083 trigger.onTriggerClick = myTriggerFn;
6084 trigger.applyTo('my-field');
6085 </code></pre>
6086  *
6087  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6088  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6089  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6090  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6091  * @constructor
6092  * Create a new TriggerField.
6093  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6094  * to the base TextField)
6095  */
6096 Roo.bootstrap.TriggerField = function(config){
6097     this.mimicing = false;
6098     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6099 };
6100
6101 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6102     /**
6103      * @cfg {String} triggerClass A CSS class to apply to the trigger
6104      */
6105      /**
6106      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6107      */
6108     hideTrigger:false,
6109
6110     /** @cfg {Boolean} grow @hide */
6111     /** @cfg {Number} growMin @hide */
6112     /** @cfg {Number} growMax @hide */
6113
6114     /**
6115      * @hide 
6116      * @method
6117      */
6118     autoSize: Roo.emptyFn,
6119     // private
6120     monitorTab : true,
6121     // private
6122     deferHeight : true,
6123
6124     
6125     actionMode : 'wrap',
6126     
6127     
6128     
6129     getAutoCreate : function(){
6130        
6131         var parent = this.parent();
6132         
6133         var align = this.parentLabelAlign();
6134         
6135         var id = Roo.id();
6136         
6137         var cfg = {
6138             cls: 'form-group' //input-group
6139         };
6140         
6141         
6142         var input =  {
6143             tag: 'input',
6144             id : id,
6145             type : this.inputType,
6146             cls : 'form-control',
6147             autocomplete: 'off',
6148             placeholder : this.placeholder || '' 
6149             
6150         };
6151         if (this.name) {
6152             input.name = this.name;
6153         }
6154         if (this.size) {
6155             input.cls += ' input-' + this.size;
6156         }
6157         
6158         if (this.disabled) {
6159             input.disabled=true;
6160         }
6161         
6162         var inputblock = input;
6163         
6164         if (this.before || this.after) {
6165             
6166             inputblock = {
6167                 cls : 'input-group',
6168                 cn :  [] 
6169             };
6170             if (this.before) {
6171                 inputblock.cn.push({
6172                     tag :'span',
6173                     cls : 'input-group-addon',
6174                     html : this.before
6175                 });
6176             }
6177             inputblock.cn.push(input);
6178             if (this.after) {
6179                 inputblock.cn.push({
6180                     tag :'span',
6181                     cls : 'input-group-addon',
6182                     html : this.after
6183                 });
6184             }
6185             
6186         };
6187         
6188         var box = {
6189             tag: 'div',
6190             cn: [
6191                 {
6192                     tag: 'input',
6193                     type : 'hidden',
6194                     cls: 'form-hidden-field'
6195                 },
6196                 inputblock
6197             ]
6198             
6199         };
6200         
6201         if(this.multiple){
6202             Roo.log('multiple');
6203             
6204             box = {
6205                 tag: 'div',
6206                 cn: [
6207                     {
6208                         tag: 'input',
6209                         type : 'hidden',
6210                         cls: 'form-hidden-field'
6211                     },
6212                     {
6213                         tag: 'ul',
6214                         cls: 'select2-choices',
6215                         cn:[
6216                             {
6217                                 tag: 'li',
6218                                 cls: 'select2-search-field',
6219                                 cn: [
6220
6221                                     inputblock
6222                                 ]
6223                             }
6224                         ]
6225                     }
6226                 ]
6227             }
6228         };
6229         
6230         var combobox = {
6231             cls: 'select2-container input-group',
6232             cn: [
6233                 box,
6234                 {
6235                     tag: 'ul',
6236                     cls: 'typeahead typeahead-long dropdown-menu',
6237                     style: 'display:none'
6238                 }
6239             ]
6240         };
6241         
6242         if(!this.multiple){
6243             combobox.cn.push({
6244                 tag :'span',
6245                 cls : 'input-group-addon btn dropdown-toggle',
6246                 cn : [
6247                     {
6248                         tag: 'span',
6249                         cls: 'caret'
6250                     },
6251                     {
6252                         tag: 'span',
6253                         cls: 'combobox-clear',
6254                         cn  : [
6255                             {
6256                                 tag : 'i',
6257                                 cls: 'icon-remove'
6258                             }
6259                         ]
6260                     }
6261                 ]
6262
6263             })
6264         }
6265         
6266         if(this.multiple){
6267             combobox.cls += ' select2-container-multi';
6268         }
6269         
6270         if (align ==='left' && this.fieldLabel.length) {
6271             
6272                 Roo.log("left and has label");
6273                 cfg.cn = [
6274                     
6275                     {
6276                         tag: 'label',
6277                         'for' :  id,
6278                         cls : 'control-label col-sm-' + this.labelWidth,
6279                         html : this.fieldLabel
6280                         
6281                     },
6282                     {
6283                         cls : "col-sm-" + (12 - this.labelWidth), 
6284                         cn: [
6285                             combobox
6286                         ]
6287                     }
6288                     
6289                 ];
6290         } else if ( this.fieldLabel.length) {
6291                 Roo.log(" label");
6292                  cfg.cn = [
6293                    
6294                     {
6295                         tag: 'label',
6296                         //cls : 'input-group-addon',
6297                         html : this.fieldLabel
6298                         
6299                     },
6300                     
6301                     combobox
6302                     
6303                 ];
6304
6305         } else {
6306             
6307                 Roo.log(" no label && no align");
6308                 cfg = combobox
6309                      
6310                 
6311         }
6312          
6313         var settings=this;
6314         ['xs','sm','md','lg'].map(function(size){
6315             if (settings[size]) {
6316                 cfg.cls += ' col-' + size + '-' + settings[size];
6317             }
6318         });
6319         
6320         return cfg;
6321         
6322     },
6323     
6324     
6325     
6326     // private
6327     onResize : function(w, h){
6328 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6329 //        if(typeof w == 'number'){
6330 //            var x = w - this.trigger.getWidth();
6331 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6332 //            this.trigger.setStyle('left', x+'px');
6333 //        }
6334     },
6335
6336     // private
6337     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6338
6339     // private
6340     getResizeEl : function(){
6341         return this.inputEl();
6342     },
6343
6344     // private
6345     getPositionEl : function(){
6346         return this.inputEl();
6347     },
6348
6349     // private
6350     alignErrorIcon : function(){
6351         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6352     },
6353
6354     // private
6355     initEvents : function(){
6356         
6357         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6358         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6359         if(!this.multiple){
6360             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6361             if(this.hideTrigger){
6362                 this.trigger.setDisplayed(false);
6363             }
6364             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6365         }
6366         
6367         if(this.multiple){
6368             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6369         }
6370         
6371         //this.trigger.addClassOnOver('x-form-trigger-over');
6372         //this.trigger.addClassOnClick('x-form-trigger-click');
6373         
6374         //if(!this.width){
6375         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6376         //}
6377     },
6378
6379     // private
6380     initTrigger : function(){
6381        
6382     },
6383
6384     // private
6385     onDestroy : function(){
6386         if(this.trigger){
6387             this.trigger.removeAllListeners();
6388           //  this.trigger.remove();
6389         }
6390         //if(this.wrap){
6391         //    this.wrap.remove();
6392         //}
6393         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6394     },
6395
6396     // private
6397     onFocus : function(){
6398         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6399         /*
6400         if(!this.mimicing){
6401             this.wrap.addClass('x-trigger-wrap-focus');
6402             this.mimicing = true;
6403             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6404             if(this.monitorTab){
6405                 this.el.on("keydown", this.checkTab, this);
6406             }
6407         }
6408         */
6409     },
6410
6411     // private
6412     checkTab : function(e){
6413         if(e.getKey() == e.TAB){
6414             this.triggerBlur();
6415         }
6416     },
6417
6418     // private
6419     onBlur : function(){
6420         // do nothing
6421     },
6422
6423     // private
6424     mimicBlur : function(e, t){
6425         /*
6426         if(!this.wrap.contains(t) && this.validateBlur()){
6427             this.triggerBlur();
6428         }
6429         */
6430     },
6431
6432     // private
6433     triggerBlur : function(){
6434         this.mimicing = false;
6435         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6436         if(this.monitorTab){
6437             this.el.un("keydown", this.checkTab, this);
6438         }
6439         //this.wrap.removeClass('x-trigger-wrap-focus');
6440         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6441     },
6442
6443     // private
6444     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6445     validateBlur : function(e, t){
6446         return true;
6447     },
6448
6449     // private
6450     onDisable : function(){
6451         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6452         //if(this.wrap){
6453         //    this.wrap.addClass('x-item-disabled');
6454         //}
6455     },
6456
6457     // private
6458     onEnable : function(){
6459         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6460         //if(this.wrap){
6461         //    this.el.removeClass('x-item-disabled');
6462         //}
6463     },
6464
6465     // private
6466     onShow : function(){
6467         var ae = this.getActionEl();
6468         
6469         if(ae){
6470             ae.dom.style.display = '';
6471             ae.dom.style.visibility = 'visible';
6472         }
6473     },
6474
6475     // private
6476     
6477     onHide : function(){
6478         var ae = this.getActionEl();
6479         ae.dom.style.display = 'none';
6480     },
6481
6482     /**
6483      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6484      * by an implementing function.
6485      * @method
6486      * @param {EventObject} e
6487      */
6488     onTriggerClick : Roo.emptyFn
6489 });
6490  /*
6491  * Based on:
6492  * Ext JS Library 1.1.1
6493  * Copyright(c) 2006-2007, Ext JS, LLC.
6494  *
6495  * Originally Released Under LGPL - original licence link has changed is not relivant.
6496  *
6497  * Fork - LGPL
6498  * <script type="text/javascript">
6499  */
6500
6501
6502 /**
6503  * @class Roo.data.SortTypes
6504  * @singleton
6505  * Defines the default sorting (casting?) comparison functions used when sorting data.
6506  */
6507 Roo.data.SortTypes = {
6508     /**
6509      * Default sort that does nothing
6510      * @param {Mixed} s The value being converted
6511      * @return {Mixed} The comparison value
6512      */
6513     none : function(s){
6514         return s;
6515     },
6516     
6517     /**
6518      * The regular expression used to strip tags
6519      * @type {RegExp}
6520      * @property
6521      */
6522     stripTagsRE : /<\/?[^>]+>/gi,
6523     
6524     /**
6525      * Strips all HTML tags to sort on text only
6526      * @param {Mixed} s The value being converted
6527      * @return {String} The comparison value
6528      */
6529     asText : function(s){
6530         return String(s).replace(this.stripTagsRE, "");
6531     },
6532     
6533     /**
6534      * Strips all HTML tags to sort on text only - Case insensitive
6535      * @param {Mixed} s The value being converted
6536      * @return {String} The comparison value
6537      */
6538     asUCText : function(s){
6539         return String(s).toUpperCase().replace(this.stripTagsRE, "");
6540     },
6541     
6542     /**
6543      * Case insensitive string
6544      * @param {Mixed} s The value being converted
6545      * @return {String} The comparison value
6546      */
6547     asUCString : function(s) {
6548         return String(s).toUpperCase();
6549     },
6550     
6551     /**
6552      * Date sorting
6553      * @param {Mixed} s The value being converted
6554      * @return {Number} The comparison value
6555      */
6556     asDate : function(s) {
6557         if(!s){
6558             return 0;
6559         }
6560         if(s instanceof Date){
6561             return s.getTime();
6562         }
6563         return Date.parse(String(s));
6564     },
6565     
6566     /**
6567      * Float sorting
6568      * @param {Mixed} s The value being converted
6569      * @return {Float} The comparison value
6570      */
6571     asFloat : function(s) {
6572         var val = parseFloat(String(s).replace(/,/g, ""));
6573         if(isNaN(val)) val = 0;
6574         return val;
6575     },
6576     
6577     /**
6578      * Integer sorting
6579      * @param {Mixed} s The value being converted
6580      * @return {Number} The comparison value
6581      */
6582     asInt : function(s) {
6583         var val = parseInt(String(s).replace(/,/g, ""));
6584         if(isNaN(val)) val = 0;
6585         return val;
6586     }
6587 };/*
6588  * Based on:
6589  * Ext JS Library 1.1.1
6590  * Copyright(c) 2006-2007, Ext JS, LLC.
6591  *
6592  * Originally Released Under LGPL - original licence link has changed is not relivant.
6593  *
6594  * Fork - LGPL
6595  * <script type="text/javascript">
6596  */
6597
6598 /**
6599 * @class Roo.data.Record
6600  * Instances of this class encapsulate both record <em>definition</em> information, and record
6601  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
6602  * to access Records cached in an {@link Roo.data.Store} object.<br>
6603  * <p>
6604  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
6605  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
6606  * objects.<br>
6607  * <p>
6608  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
6609  * @constructor
6610  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
6611  * {@link #create}. The parameters are the same.
6612  * @param {Array} data An associative Array of data values keyed by the field name.
6613  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
6614  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
6615  * not specified an integer id is generated.
6616  */
6617 Roo.data.Record = function(data, id){
6618     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
6619     this.data = data;
6620 };
6621
6622 /**
6623  * Generate a constructor for a specific record layout.
6624  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
6625  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
6626  * Each field definition object may contain the following properties: <ul>
6627  * <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,
6628  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
6629  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
6630  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
6631  * is being used, then this is a string containing the javascript expression to reference the data relative to 
6632  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
6633  * to the data item relative to the record element. If the mapping expression is the same as the field name,
6634  * this may be omitted.</p></li>
6635  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
6636  * <ul><li>auto (Default, implies no conversion)</li>
6637  * <li>string</li>
6638  * <li>int</li>
6639  * <li>float</li>
6640  * <li>boolean</li>
6641  * <li>date</li></ul></p></li>
6642  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6643  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6644  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6645  * by the Reader into an object that will be stored in the Record. It is passed the
6646  * following parameters:<ul>
6647  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6648  * </ul></p></li>
6649  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6650  * </ul>
6651  * <br>usage:<br><pre><code>
6652 var TopicRecord = Roo.data.Record.create(
6653     {name: 'title', mapping: 'topic_title'},
6654     {name: 'author', mapping: 'username'},
6655     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6656     {name: 'lastPost', mapping: 'post_time', type: 'date'},
6657     {name: 'lastPoster', mapping: 'user2'},
6658     {name: 'excerpt', mapping: 'post_text'}
6659 );
6660
6661 var myNewRecord = new TopicRecord({
6662     title: 'Do my job please',
6663     author: 'noobie',
6664     totalPosts: 1,
6665     lastPost: new Date(),
6666     lastPoster: 'Animal',
6667     excerpt: 'No way dude!'
6668 });
6669 myStore.add(myNewRecord);
6670 </code></pre>
6671  * @method create
6672  * @static
6673  */
6674 Roo.data.Record.create = function(o){
6675     var f = function(){
6676         f.superclass.constructor.apply(this, arguments);
6677     };
6678     Roo.extend(f, Roo.data.Record);
6679     var p = f.prototype;
6680     p.fields = new Roo.util.MixedCollection(false, function(field){
6681         return field.name;
6682     });
6683     for(var i = 0, len = o.length; i < len; i++){
6684         p.fields.add(new Roo.data.Field(o[i]));
6685     }
6686     f.getField = function(name){
6687         return p.fields.get(name);  
6688     };
6689     return f;
6690 };
6691
6692 Roo.data.Record.AUTO_ID = 1000;
6693 Roo.data.Record.EDIT = 'edit';
6694 Roo.data.Record.REJECT = 'reject';
6695 Roo.data.Record.COMMIT = 'commit';
6696
6697 Roo.data.Record.prototype = {
6698     /**
6699      * Readonly flag - true if this record has been modified.
6700      * @type Boolean
6701      */
6702     dirty : false,
6703     editing : false,
6704     error: null,
6705     modified: null,
6706
6707     // private
6708     join : function(store){
6709         this.store = store;
6710     },
6711
6712     /**
6713      * Set the named field to the specified value.
6714      * @param {String} name The name of the field to set.
6715      * @param {Object} value The value to set the field to.
6716      */
6717     set : function(name, value){
6718         if(this.data[name] == value){
6719             return;
6720         }
6721         this.dirty = true;
6722         if(!this.modified){
6723             this.modified = {};
6724         }
6725         if(typeof this.modified[name] == 'undefined'){
6726             this.modified[name] = this.data[name];
6727         }
6728         this.data[name] = value;
6729         if(!this.editing && this.store){
6730             this.store.afterEdit(this);
6731         }       
6732     },
6733
6734     /**
6735      * Get the value of the named field.
6736      * @param {String} name The name of the field to get the value of.
6737      * @return {Object} The value of the field.
6738      */
6739     get : function(name){
6740         return this.data[name]; 
6741     },
6742
6743     // private
6744     beginEdit : function(){
6745         this.editing = true;
6746         this.modified = {}; 
6747     },
6748
6749     // private
6750     cancelEdit : function(){
6751         this.editing = false;
6752         delete this.modified;
6753     },
6754
6755     // private
6756     endEdit : function(){
6757         this.editing = false;
6758         if(this.dirty && this.store){
6759             this.store.afterEdit(this);
6760         }
6761     },
6762
6763     /**
6764      * Usually called by the {@link Roo.data.Store} which owns the Record.
6765      * Rejects all changes made to the Record since either creation, or the last commit operation.
6766      * Modified fields are reverted to their original values.
6767      * <p>
6768      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6769      * of reject operations.
6770      */
6771     reject : function(){
6772         var m = this.modified;
6773         for(var n in m){
6774             if(typeof m[n] != "function"){
6775                 this.data[n] = m[n];
6776             }
6777         }
6778         this.dirty = false;
6779         delete this.modified;
6780         this.editing = false;
6781         if(this.store){
6782             this.store.afterReject(this);
6783         }
6784     },
6785
6786     /**
6787      * Usually called by the {@link Roo.data.Store} which owns the Record.
6788      * Commits all changes made to the Record since either creation, or the last commit operation.
6789      * <p>
6790      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6791      * of commit operations.
6792      */
6793     commit : function(){
6794         this.dirty = false;
6795         delete this.modified;
6796         this.editing = false;
6797         if(this.store){
6798             this.store.afterCommit(this);
6799         }
6800     },
6801
6802     // private
6803     hasError : function(){
6804         return this.error != null;
6805     },
6806
6807     // private
6808     clearError : function(){
6809         this.error = null;
6810     },
6811
6812     /**
6813      * Creates a copy of this record.
6814      * @param {String} id (optional) A new record id if you don't want to use this record's id
6815      * @return {Record}
6816      */
6817     copy : function(newId) {
6818         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6819     }
6820 };/*
6821  * Based on:
6822  * Ext JS Library 1.1.1
6823  * Copyright(c) 2006-2007, Ext JS, LLC.
6824  *
6825  * Originally Released Under LGPL - original licence link has changed is not relivant.
6826  *
6827  * Fork - LGPL
6828  * <script type="text/javascript">
6829  */
6830
6831
6832
6833 /**
6834  * @class Roo.data.Store
6835  * @extends Roo.util.Observable
6836  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6837  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6838  * <p>
6839  * 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
6840  * has no knowledge of the format of the data returned by the Proxy.<br>
6841  * <p>
6842  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6843  * instances from the data object. These records are cached and made available through accessor functions.
6844  * @constructor
6845  * Creates a new Store.
6846  * @param {Object} config A config object containing the objects needed for the Store to access data,
6847  * and read the data into Records.
6848  */
6849 Roo.data.Store = function(config){
6850     this.data = new Roo.util.MixedCollection(false);
6851     this.data.getKey = function(o){
6852         return o.id;
6853     };
6854     this.baseParams = {};
6855     // private
6856     this.paramNames = {
6857         "start" : "start",
6858         "limit" : "limit",
6859         "sort" : "sort",
6860         "dir" : "dir",
6861         "multisort" : "_multisort"
6862     };
6863
6864     if(config && config.data){
6865         this.inlineData = config.data;
6866         delete config.data;
6867     }
6868
6869     Roo.apply(this, config);
6870     
6871     if(this.reader){ // reader passed
6872         this.reader = Roo.factory(this.reader, Roo.data);
6873         this.reader.xmodule = this.xmodule || false;
6874         if(!this.recordType){
6875             this.recordType = this.reader.recordType;
6876         }
6877         if(this.reader.onMetaChange){
6878             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6879         }
6880     }
6881
6882     if(this.recordType){
6883         this.fields = this.recordType.prototype.fields;
6884     }
6885     this.modified = [];
6886
6887     this.addEvents({
6888         /**
6889          * @event datachanged
6890          * Fires when the data cache has changed, and a widget which is using this Store
6891          * as a Record cache should refresh its view.
6892          * @param {Store} this
6893          */
6894         datachanged : true,
6895         /**
6896          * @event metachange
6897          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6898          * @param {Store} this
6899          * @param {Object} meta The JSON metadata
6900          */
6901         metachange : true,
6902         /**
6903          * @event add
6904          * Fires when Records have been added to the Store
6905          * @param {Store} this
6906          * @param {Roo.data.Record[]} records The array of Records added
6907          * @param {Number} index The index at which the record(s) were added
6908          */
6909         add : true,
6910         /**
6911          * @event remove
6912          * Fires when a Record has been removed from the Store
6913          * @param {Store} this
6914          * @param {Roo.data.Record} record The Record that was removed
6915          * @param {Number} index The index at which the record was removed
6916          */
6917         remove : true,
6918         /**
6919          * @event update
6920          * Fires when a Record has been updated
6921          * @param {Store} this
6922          * @param {Roo.data.Record} record The Record that was updated
6923          * @param {String} operation The update operation being performed.  Value may be one of:
6924          * <pre><code>
6925  Roo.data.Record.EDIT
6926  Roo.data.Record.REJECT
6927  Roo.data.Record.COMMIT
6928          * </code></pre>
6929          */
6930         update : true,
6931         /**
6932          * @event clear
6933          * Fires when the data cache has been cleared.
6934          * @param {Store} this
6935          */
6936         clear : true,
6937         /**
6938          * @event beforeload
6939          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6940          * the load action will be canceled.
6941          * @param {Store} this
6942          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6943          */
6944         beforeload : true,
6945         /**
6946          * @event beforeloadadd
6947          * Fires after a new set of Records has been loaded.
6948          * @param {Store} this
6949          * @param {Roo.data.Record[]} records The Records that were loaded
6950          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6951          */
6952         beforeloadadd : true,
6953         /**
6954          * @event load
6955          * Fires after a new set of Records has been loaded, before they are added to the store.
6956          * @param {Store} this
6957          * @param {Roo.data.Record[]} records The Records that were loaded
6958          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6959          * @params {Object} return from reader
6960          */
6961         load : true,
6962         /**
6963          * @event loadexception
6964          * Fires if an exception occurs in the Proxy during loading.
6965          * Called with the signature of the Proxy's "loadexception" event.
6966          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6967          * 
6968          * @param {Proxy} 
6969          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6970          * @param {Object} load options 
6971          * @param {Object} jsonData from your request (normally this contains the Exception)
6972          */
6973         loadexception : true
6974     });
6975     
6976     if(this.proxy){
6977         this.proxy = Roo.factory(this.proxy, Roo.data);
6978         this.proxy.xmodule = this.xmodule || false;
6979         this.relayEvents(this.proxy,  ["loadexception"]);
6980     }
6981     this.sortToggle = {};
6982     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6983
6984     Roo.data.Store.superclass.constructor.call(this);
6985
6986     if(this.inlineData){
6987         this.loadData(this.inlineData);
6988         delete this.inlineData;
6989     }
6990 };
6991
6992 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6993      /**
6994     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6995     * without a remote query - used by combo/forms at present.
6996     */
6997     
6998     /**
6999     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7000     */
7001     /**
7002     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7003     */
7004     /**
7005     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7006     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7007     */
7008     /**
7009     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7010     * on any HTTP request
7011     */
7012     /**
7013     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7014     */
7015     /**
7016     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7017     */
7018     multiSort: false,
7019     /**
7020     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7021     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7022     */
7023     remoteSort : false,
7024
7025     /**
7026     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7027      * loaded or when a record is removed. (defaults to false).
7028     */
7029     pruneModifiedRecords : false,
7030
7031     // private
7032     lastOptions : null,
7033
7034     /**
7035      * Add Records to the Store and fires the add event.
7036      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7037      */
7038     add : function(records){
7039         records = [].concat(records);
7040         for(var i = 0, len = records.length; i < len; i++){
7041             records[i].join(this);
7042         }
7043         var index = this.data.length;
7044         this.data.addAll(records);
7045         this.fireEvent("add", this, records, index);
7046     },
7047
7048     /**
7049      * Remove a Record from the Store and fires the remove event.
7050      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7051      */
7052     remove : function(record){
7053         var index = this.data.indexOf(record);
7054         this.data.removeAt(index);
7055         if(this.pruneModifiedRecords){
7056             this.modified.remove(record);
7057         }
7058         this.fireEvent("remove", this, record, index);
7059     },
7060
7061     /**
7062      * Remove all Records from the Store and fires the clear event.
7063      */
7064     removeAll : function(){
7065         this.data.clear();
7066         if(this.pruneModifiedRecords){
7067             this.modified = [];
7068         }
7069         this.fireEvent("clear", this);
7070     },
7071
7072     /**
7073      * Inserts Records to the Store at the given index and fires the add event.
7074      * @param {Number} index The start index at which to insert the passed Records.
7075      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7076      */
7077     insert : function(index, records){
7078         records = [].concat(records);
7079         for(var i = 0, len = records.length; i < len; i++){
7080             this.data.insert(index, records[i]);
7081             records[i].join(this);
7082         }
7083         this.fireEvent("add", this, records, index);
7084     },
7085
7086     /**
7087      * Get the index within the cache of the passed Record.
7088      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7089      * @return {Number} The index of the passed Record. Returns -1 if not found.
7090      */
7091     indexOf : function(record){
7092         return this.data.indexOf(record);
7093     },
7094
7095     /**
7096      * Get the index within the cache of the Record with the passed id.
7097      * @param {String} id The id of the Record to find.
7098      * @return {Number} The index of the Record. Returns -1 if not found.
7099      */
7100     indexOfId : function(id){
7101         return this.data.indexOfKey(id);
7102     },
7103
7104     /**
7105      * Get the Record with the specified id.
7106      * @param {String} id The id of the Record to find.
7107      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7108      */
7109     getById : function(id){
7110         return this.data.key(id);
7111     },
7112
7113     /**
7114      * Get the Record at the specified index.
7115      * @param {Number} index The index of the Record to find.
7116      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7117      */
7118     getAt : function(index){
7119         return this.data.itemAt(index);
7120     },
7121
7122     /**
7123      * Returns a range of Records between specified indices.
7124      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7125      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7126      * @return {Roo.data.Record[]} An array of Records
7127      */
7128     getRange : function(start, end){
7129         return this.data.getRange(start, end);
7130     },
7131
7132     // private
7133     storeOptions : function(o){
7134         o = Roo.apply({}, o);
7135         delete o.callback;
7136         delete o.scope;
7137         this.lastOptions = o;
7138     },
7139
7140     /**
7141      * Loads the Record cache from the configured Proxy using the configured Reader.
7142      * <p>
7143      * If using remote paging, then the first load call must specify the <em>start</em>
7144      * and <em>limit</em> properties in the options.params property to establish the initial
7145      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7146      * <p>
7147      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7148      * and this call will return before the new data has been loaded. Perform any post-processing
7149      * in a callback function, or in a "load" event handler.</strong>
7150      * <p>
7151      * @param {Object} options An object containing properties which control loading options:<ul>
7152      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7153      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7154      * passed the following arguments:<ul>
7155      * <li>r : Roo.data.Record[]</li>
7156      * <li>options: Options object from the load call</li>
7157      * <li>success: Boolean success indicator</li></ul></li>
7158      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7159      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7160      * </ul>
7161      */
7162     load : function(options){
7163         options = options || {};
7164         if(this.fireEvent("beforeload", this, options) !== false){
7165             this.storeOptions(options);
7166             var p = Roo.apply(options.params || {}, this.baseParams);
7167             // if meta was not loaded from remote source.. try requesting it.
7168             if (!this.reader.metaFromRemote) {
7169                 p._requestMeta = 1;
7170             }
7171             if(this.sortInfo && this.remoteSort){
7172                 var pn = this.paramNames;
7173                 p[pn["sort"]] = this.sortInfo.field;
7174                 p[pn["dir"]] = this.sortInfo.direction;
7175             }
7176             if (this.multiSort) {
7177                 var pn = this.paramNames;
7178                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7179             }
7180             
7181             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7182         }
7183     },
7184
7185     /**
7186      * Reloads the Record cache from the configured Proxy using the configured Reader and
7187      * the options from the last load operation performed.
7188      * @param {Object} options (optional) An object containing properties which may override the options
7189      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7190      * the most recently used options are reused).
7191      */
7192     reload : function(options){
7193         this.load(Roo.applyIf(options||{}, this.lastOptions));
7194     },
7195
7196     // private
7197     // Called as a callback by the Reader during a load operation.
7198     loadRecords : function(o, options, success){
7199         if(!o || success === false){
7200             if(success !== false){
7201                 this.fireEvent("load", this, [], options, o);
7202             }
7203             if(options.callback){
7204                 options.callback.call(options.scope || this, [], options, false);
7205             }
7206             return;
7207         }
7208         // if data returned failure - throw an exception.
7209         if (o.success === false) {
7210             // show a message if no listener is registered.
7211             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7212                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7213             }
7214             // loadmask wil be hooked into this..
7215             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7216             return;
7217         }
7218         var r = o.records, t = o.totalRecords || r.length;
7219         
7220         this.fireEvent("beforeloadadd", this, r, options, o);
7221         
7222         if(!options || options.add !== true){
7223             if(this.pruneModifiedRecords){
7224                 this.modified = [];
7225             }
7226             for(var i = 0, len = r.length; i < len; i++){
7227                 r[i].join(this);
7228             }
7229             if(this.snapshot){
7230                 this.data = this.snapshot;
7231                 delete this.snapshot;
7232             }
7233             this.data.clear();
7234             this.data.addAll(r);
7235             this.totalLength = t;
7236             this.applySort();
7237             this.fireEvent("datachanged", this);
7238         }else{
7239             this.totalLength = Math.max(t, this.data.length+r.length);
7240             this.add(r);
7241         }
7242         this.fireEvent("load", this, r, options, o);
7243         if(options.callback){
7244             options.callback.call(options.scope || this, r, options, true);
7245         }
7246     },
7247
7248
7249     /**
7250      * Loads data from a passed data block. A Reader which understands the format of the data
7251      * must have been configured in the constructor.
7252      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7253      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7254      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7255      */
7256     loadData : function(o, append){
7257         var r = this.reader.readRecords(o);
7258         this.loadRecords(r, {add: append}, true);
7259     },
7260
7261     /**
7262      * Gets the number of cached records.
7263      * <p>
7264      * <em>If using paging, this may not be the total size of the dataset. If the data object
7265      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7266      * the data set size</em>
7267      */
7268     getCount : function(){
7269         return this.data.length || 0;
7270     },
7271
7272     /**
7273      * Gets the total number of records in the dataset as returned by the server.
7274      * <p>
7275      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7276      * the dataset size</em>
7277      */
7278     getTotalCount : function(){
7279         return this.totalLength || 0;
7280     },
7281
7282     /**
7283      * Returns the sort state of the Store as an object with two properties:
7284      * <pre><code>
7285  field {String} The name of the field by which the Records are sorted
7286  direction {String} The sort order, "ASC" or "DESC"
7287      * </code></pre>
7288      */
7289     getSortState : function(){
7290         return this.sortInfo;
7291     },
7292
7293     // private
7294     applySort : function(){
7295         if(this.sortInfo && !this.remoteSort){
7296             var s = this.sortInfo, f = s.field;
7297             var st = this.fields.get(f).sortType;
7298             var fn = function(r1, r2){
7299                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7300                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7301             };
7302             this.data.sort(s.direction, fn);
7303             if(this.snapshot && this.snapshot != this.data){
7304                 this.snapshot.sort(s.direction, fn);
7305             }
7306         }
7307     },
7308
7309     /**
7310      * Sets the default sort column and order to be used by the next load operation.
7311      * @param {String} fieldName The name of the field to sort by.
7312      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7313      */
7314     setDefaultSort : function(field, dir){
7315         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7316     },
7317
7318     /**
7319      * Sort the Records.
7320      * If remote sorting is used, the sort is performed on the server, and the cache is
7321      * reloaded. If local sorting is used, the cache is sorted internally.
7322      * @param {String} fieldName The name of the field to sort by.
7323      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7324      */
7325     sort : function(fieldName, dir){
7326         var f = this.fields.get(fieldName);
7327         if(!dir){
7328             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7329             
7330             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7331                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7332             }else{
7333                 dir = f.sortDir;
7334             }
7335         }
7336         this.sortToggle[f.name] = dir;
7337         this.sortInfo = {field: f.name, direction: dir};
7338         if(!this.remoteSort){
7339             this.applySort();
7340             this.fireEvent("datachanged", this);
7341         }else{
7342             this.load(this.lastOptions);
7343         }
7344     },
7345
7346     /**
7347      * Calls the specified function for each of the Records in the cache.
7348      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7349      * Returning <em>false</em> aborts and exits the iteration.
7350      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7351      */
7352     each : function(fn, scope){
7353         this.data.each(fn, scope);
7354     },
7355
7356     /**
7357      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7358      * (e.g., during paging).
7359      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7360      */
7361     getModifiedRecords : function(){
7362         return this.modified;
7363     },
7364
7365     // private
7366     createFilterFn : function(property, value, anyMatch){
7367         if(!value.exec){ // not a regex
7368             value = String(value);
7369             if(value.length == 0){
7370                 return false;
7371             }
7372             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7373         }
7374         return function(r){
7375             return value.test(r.data[property]);
7376         };
7377     },
7378
7379     /**
7380      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7381      * @param {String} property A field on your records
7382      * @param {Number} start The record index to start at (defaults to 0)
7383      * @param {Number} end The last record index to include (defaults to length - 1)
7384      * @return {Number} The sum
7385      */
7386     sum : function(property, start, end){
7387         var rs = this.data.items, v = 0;
7388         start = start || 0;
7389         end = (end || end === 0) ? end : rs.length-1;
7390
7391         for(var i = start; i <= end; i++){
7392             v += (rs[i].data[property] || 0);
7393         }
7394         return v;
7395     },
7396
7397     /**
7398      * Filter the records by a specified property.
7399      * @param {String} field A field on your records
7400      * @param {String/RegExp} value Either a string that the field
7401      * should start with or a RegExp to test against the field
7402      * @param {Boolean} anyMatch True to match any part not just the beginning
7403      */
7404     filter : function(property, value, anyMatch){
7405         var fn = this.createFilterFn(property, value, anyMatch);
7406         return fn ? this.filterBy(fn) : this.clearFilter();
7407     },
7408
7409     /**
7410      * Filter by a function. The specified function will be called with each
7411      * record in this data source. If the function returns true the record is included,
7412      * otherwise it is filtered.
7413      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7414      * @param {Object} scope (optional) The scope of the function (defaults to this)
7415      */
7416     filterBy : function(fn, scope){
7417         this.snapshot = this.snapshot || this.data;
7418         this.data = this.queryBy(fn, scope||this);
7419         this.fireEvent("datachanged", this);
7420     },
7421
7422     /**
7423      * Query the records by a specified property.
7424      * @param {String} field A field on your records
7425      * @param {String/RegExp} value Either a string that the field
7426      * should start with or a RegExp to test against the field
7427      * @param {Boolean} anyMatch True to match any part not just the beginning
7428      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7429      */
7430     query : function(property, value, anyMatch){
7431         var fn = this.createFilterFn(property, value, anyMatch);
7432         return fn ? this.queryBy(fn) : this.data.clone();
7433     },
7434
7435     /**
7436      * Query by a function. The specified function will be called with each
7437      * record in this data source. If the function returns true the record is included
7438      * in the results.
7439      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7440      * @param {Object} scope (optional) The scope of the function (defaults to this)
7441       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7442      **/
7443     queryBy : function(fn, scope){
7444         var data = this.snapshot || this.data;
7445         return data.filterBy(fn, scope||this);
7446     },
7447
7448     /**
7449      * Collects unique values for a particular dataIndex from this store.
7450      * @param {String} dataIndex The property to collect
7451      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7452      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7453      * @return {Array} An array of the unique values
7454      **/
7455     collect : function(dataIndex, allowNull, bypassFilter){
7456         var d = (bypassFilter === true && this.snapshot) ?
7457                 this.snapshot.items : this.data.items;
7458         var v, sv, r = [], l = {};
7459         for(var i = 0, len = d.length; i < len; i++){
7460             v = d[i].data[dataIndex];
7461             sv = String(v);
7462             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7463                 l[sv] = true;
7464                 r[r.length] = v;
7465             }
7466         }
7467         return r;
7468     },
7469
7470     /**
7471      * Revert to a view of the Record cache with no filtering applied.
7472      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7473      */
7474     clearFilter : function(suppressEvent){
7475         if(this.snapshot && this.snapshot != this.data){
7476             this.data = this.snapshot;
7477             delete this.snapshot;
7478             if(suppressEvent !== true){
7479                 this.fireEvent("datachanged", this);
7480             }
7481         }
7482     },
7483
7484     // private
7485     afterEdit : function(record){
7486         if(this.modified.indexOf(record) == -1){
7487             this.modified.push(record);
7488         }
7489         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7490     },
7491     
7492     // private
7493     afterReject : function(record){
7494         this.modified.remove(record);
7495         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7496     },
7497
7498     // private
7499     afterCommit : function(record){
7500         this.modified.remove(record);
7501         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7502     },
7503
7504     /**
7505      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7506      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7507      */
7508     commitChanges : function(){
7509         var m = this.modified.slice(0);
7510         this.modified = [];
7511         for(var i = 0, len = m.length; i < len; i++){
7512             m[i].commit();
7513         }
7514     },
7515
7516     /**
7517      * Cancel outstanding changes on all changed records.
7518      */
7519     rejectChanges : function(){
7520         var m = this.modified.slice(0);
7521         this.modified = [];
7522         for(var i = 0, len = m.length; i < len; i++){
7523             m[i].reject();
7524         }
7525     },
7526
7527     onMetaChange : function(meta, rtype, o){
7528         this.recordType = rtype;
7529         this.fields = rtype.prototype.fields;
7530         delete this.snapshot;
7531         this.sortInfo = meta.sortInfo || this.sortInfo;
7532         this.modified = [];
7533         this.fireEvent('metachange', this, this.reader.meta);
7534     },
7535     
7536     moveIndex : function(data, type)
7537     {
7538         var index = this.indexOf(data);
7539         
7540         var newIndex = index + type;
7541         
7542         this.remove(data);
7543         
7544         this.insert(newIndex, data);
7545         
7546     }
7547 });/*
7548  * Based on:
7549  * Ext JS Library 1.1.1
7550  * Copyright(c) 2006-2007, Ext JS, LLC.
7551  *
7552  * Originally Released Under LGPL - original licence link has changed is not relivant.
7553  *
7554  * Fork - LGPL
7555  * <script type="text/javascript">
7556  */
7557
7558 /**
7559  * @class Roo.data.SimpleStore
7560  * @extends Roo.data.Store
7561  * Small helper class to make creating Stores from Array data easier.
7562  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7563  * @cfg {Array} fields An array of field definition objects, or field name strings.
7564  * @cfg {Array} data The multi-dimensional array of data
7565  * @constructor
7566  * @param {Object} config
7567  */
7568 Roo.data.SimpleStore = function(config){
7569     Roo.data.SimpleStore.superclass.constructor.call(this, {
7570         isLocal : true,
7571         reader: new Roo.data.ArrayReader({
7572                 id: config.id
7573             },
7574             Roo.data.Record.create(config.fields)
7575         ),
7576         proxy : new Roo.data.MemoryProxy(config.data)
7577     });
7578     this.load();
7579 };
7580 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7581  * Based on:
7582  * Ext JS Library 1.1.1
7583  * Copyright(c) 2006-2007, Ext JS, LLC.
7584  *
7585  * Originally Released Under LGPL - original licence link has changed is not relivant.
7586  *
7587  * Fork - LGPL
7588  * <script type="text/javascript">
7589  */
7590
7591 /**
7592 /**
7593  * @extends Roo.data.Store
7594  * @class Roo.data.JsonStore
7595  * Small helper class to make creating Stores for JSON data easier. <br/>
7596 <pre><code>
7597 var store = new Roo.data.JsonStore({
7598     url: 'get-images.php',
7599     root: 'images',
7600     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
7601 });
7602 </code></pre>
7603  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
7604  * JsonReader and HttpProxy (unless inline data is provided).</b>
7605  * @cfg {Array} fields An array of field definition objects, or field name strings.
7606  * @constructor
7607  * @param {Object} config
7608  */
7609 Roo.data.JsonStore = function(c){
7610     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
7611         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
7612         reader: new Roo.data.JsonReader(c, c.fields)
7613     }));
7614 };
7615 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
7616  * Based on:
7617  * Ext JS Library 1.1.1
7618  * Copyright(c) 2006-2007, Ext JS, LLC.
7619  *
7620  * Originally Released Under LGPL - original licence link has changed is not relivant.
7621  *
7622  * Fork - LGPL
7623  * <script type="text/javascript">
7624  */
7625
7626  
7627 Roo.data.Field = function(config){
7628     if(typeof config == "string"){
7629         config = {name: config};
7630     }
7631     Roo.apply(this, config);
7632     
7633     if(!this.type){
7634         this.type = "auto";
7635     }
7636     
7637     var st = Roo.data.SortTypes;
7638     // named sortTypes are supported, here we look them up
7639     if(typeof this.sortType == "string"){
7640         this.sortType = st[this.sortType];
7641     }
7642     
7643     // set default sortType for strings and dates
7644     if(!this.sortType){
7645         switch(this.type){
7646             case "string":
7647                 this.sortType = st.asUCString;
7648                 break;
7649             case "date":
7650                 this.sortType = st.asDate;
7651                 break;
7652             default:
7653                 this.sortType = st.none;
7654         }
7655     }
7656
7657     // define once
7658     var stripRe = /[\$,%]/g;
7659
7660     // prebuilt conversion function for this field, instead of
7661     // switching every time we're reading a value
7662     if(!this.convert){
7663         var cv, dateFormat = this.dateFormat;
7664         switch(this.type){
7665             case "":
7666             case "auto":
7667             case undefined:
7668                 cv = function(v){ return v; };
7669                 break;
7670             case "string":
7671                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7672                 break;
7673             case "int":
7674                 cv = function(v){
7675                     return v !== undefined && v !== null && v !== '' ?
7676                            parseInt(String(v).replace(stripRe, ""), 10) : '';
7677                     };
7678                 break;
7679             case "float":
7680                 cv = function(v){
7681                     return v !== undefined && v !== null && v !== '' ?
7682                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
7683                     };
7684                 break;
7685             case "bool":
7686             case "boolean":
7687                 cv = function(v){ return v === true || v === "true" || v == 1; };
7688                 break;
7689             case "date":
7690                 cv = function(v){
7691                     if(!v){
7692                         return '';
7693                     }
7694                     if(v instanceof Date){
7695                         return v;
7696                     }
7697                     if(dateFormat){
7698                         if(dateFormat == "timestamp"){
7699                             return new Date(v*1000);
7700                         }
7701                         return Date.parseDate(v, dateFormat);
7702                     }
7703                     var parsed = Date.parse(v);
7704                     return parsed ? new Date(parsed) : null;
7705                 };
7706              break;
7707             
7708         }
7709         this.convert = cv;
7710     }
7711 };
7712
7713 Roo.data.Field.prototype = {
7714     dateFormat: null,
7715     defaultValue: "",
7716     mapping: null,
7717     sortType : null,
7718     sortDir : "ASC"
7719 };/*
7720  * Based on:
7721  * Ext JS Library 1.1.1
7722  * Copyright(c) 2006-2007, Ext JS, LLC.
7723  *
7724  * Originally Released Under LGPL - original licence link has changed is not relivant.
7725  *
7726  * Fork - LGPL
7727  * <script type="text/javascript">
7728  */
7729  
7730 // Base class for reading structured data from a data source.  This class is intended to be
7731 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7732
7733 /**
7734  * @class Roo.data.DataReader
7735  * Base class for reading structured data from a data source.  This class is intended to be
7736  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7737  */
7738
7739 Roo.data.DataReader = function(meta, recordType){
7740     
7741     this.meta = meta;
7742     
7743     this.recordType = recordType instanceof Array ? 
7744         Roo.data.Record.create(recordType) : recordType;
7745 };
7746
7747 Roo.data.DataReader.prototype = {
7748      /**
7749      * Create an empty record
7750      * @param {Object} data (optional) - overlay some values
7751      * @return {Roo.data.Record} record created.
7752      */
7753     newRow :  function(d) {
7754         var da =  {};
7755         this.recordType.prototype.fields.each(function(c) {
7756             switch( c.type) {
7757                 case 'int' : da[c.name] = 0; break;
7758                 case 'date' : da[c.name] = new Date(); break;
7759                 case 'float' : da[c.name] = 0.0; break;
7760                 case 'boolean' : da[c.name] = false; break;
7761                 default : da[c.name] = ""; break;
7762             }
7763             
7764         });
7765         return new this.recordType(Roo.apply(da, d));
7766     }
7767     
7768 };/*
7769  * Based on:
7770  * Ext JS Library 1.1.1
7771  * Copyright(c) 2006-2007, Ext JS, LLC.
7772  *
7773  * Originally Released Under LGPL - original licence link has changed is not relivant.
7774  *
7775  * Fork - LGPL
7776  * <script type="text/javascript">
7777  */
7778
7779 /**
7780  * @class Roo.data.DataProxy
7781  * @extends Roo.data.Observable
7782  * This class is an abstract base class for implementations which provide retrieval of
7783  * unformatted data objects.<br>
7784  * <p>
7785  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7786  * (of the appropriate type which knows how to parse the data object) to provide a block of
7787  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7788  * <p>
7789  * Custom implementations must implement the load method as described in
7790  * {@link Roo.data.HttpProxy#load}.
7791  */
7792 Roo.data.DataProxy = function(){
7793     this.addEvents({
7794         /**
7795          * @event beforeload
7796          * Fires before a network request is made to retrieve a data object.
7797          * @param {Object} This DataProxy object.
7798          * @param {Object} params The params parameter to the load function.
7799          */
7800         beforeload : true,
7801         /**
7802          * @event load
7803          * Fires before the load method's callback is called.
7804          * @param {Object} This DataProxy object.
7805          * @param {Object} o The data object.
7806          * @param {Object} arg The callback argument object passed to the load function.
7807          */
7808         load : true,
7809         /**
7810          * @event loadexception
7811          * Fires if an Exception occurs during data retrieval.
7812          * @param {Object} This DataProxy object.
7813          * @param {Object} o The data object.
7814          * @param {Object} arg The callback argument object passed to the load function.
7815          * @param {Object} e The Exception.
7816          */
7817         loadexception : true
7818     });
7819     Roo.data.DataProxy.superclass.constructor.call(this);
7820 };
7821
7822 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7823
7824     /**
7825      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7826      */
7827 /*
7828  * Based on:
7829  * Ext JS Library 1.1.1
7830  * Copyright(c) 2006-2007, Ext JS, LLC.
7831  *
7832  * Originally Released Under LGPL - original licence link has changed is not relivant.
7833  *
7834  * Fork - LGPL
7835  * <script type="text/javascript">
7836  */
7837 /**
7838  * @class Roo.data.MemoryProxy
7839  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7840  * to the Reader when its load method is called.
7841  * @constructor
7842  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7843  */
7844 Roo.data.MemoryProxy = function(data){
7845     if (data.data) {
7846         data = data.data;
7847     }
7848     Roo.data.MemoryProxy.superclass.constructor.call(this);
7849     this.data = data;
7850 };
7851
7852 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7853     /**
7854      * Load data from the requested source (in this case an in-memory
7855      * data object passed to the constructor), read the data object into
7856      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7857      * process that block using the passed callback.
7858      * @param {Object} params This parameter is not used by the MemoryProxy class.
7859      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7860      * object into a block of Roo.data.Records.
7861      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7862      * The function must be passed <ul>
7863      * <li>The Record block object</li>
7864      * <li>The "arg" argument from the load function</li>
7865      * <li>A boolean success indicator</li>
7866      * </ul>
7867      * @param {Object} scope The scope in which to call the callback
7868      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7869      */
7870     load : function(params, reader, callback, scope, arg){
7871         params = params || {};
7872         var result;
7873         try {
7874             result = reader.readRecords(this.data);
7875         }catch(e){
7876             this.fireEvent("loadexception", this, arg, null, e);
7877             callback.call(scope, null, arg, false);
7878             return;
7879         }
7880         callback.call(scope, result, arg, true);
7881     },
7882     
7883     // private
7884     update : function(params, records){
7885         
7886     }
7887 });/*
7888  * Based on:
7889  * Ext JS Library 1.1.1
7890  * Copyright(c) 2006-2007, Ext JS, LLC.
7891  *
7892  * Originally Released Under LGPL - original licence link has changed is not relivant.
7893  *
7894  * Fork - LGPL
7895  * <script type="text/javascript">
7896  */
7897 /**
7898  * @class Roo.data.HttpProxy
7899  * @extends Roo.data.DataProxy
7900  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7901  * configured to reference a certain URL.<br><br>
7902  * <p>
7903  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7904  * from which the running page was served.<br><br>
7905  * <p>
7906  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7907  * <p>
7908  * Be aware that to enable the browser to parse an XML document, the server must set
7909  * the Content-Type header in the HTTP response to "text/xml".
7910  * @constructor
7911  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7912  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7913  * will be used to make the request.
7914  */
7915 Roo.data.HttpProxy = function(conn){
7916     Roo.data.HttpProxy.superclass.constructor.call(this);
7917     // is conn a conn config or a real conn?
7918     this.conn = conn;
7919     this.useAjax = !conn || !conn.events;
7920   
7921 };
7922
7923 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7924     // thse are take from connection...
7925     
7926     /**
7927      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7928      */
7929     /**
7930      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7931      * extra parameters to each request made by this object. (defaults to undefined)
7932      */
7933     /**
7934      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7935      *  to each request made by this object. (defaults to undefined)
7936      */
7937     /**
7938      * @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)
7939      */
7940     /**
7941      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7942      */
7943      /**
7944      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7945      * @type Boolean
7946      */
7947   
7948
7949     /**
7950      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7951      * @type Boolean
7952      */
7953     /**
7954      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7955      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7956      * a finer-grained basis than the DataProxy events.
7957      */
7958     getConnection : function(){
7959         return this.useAjax ? Roo.Ajax : this.conn;
7960     },
7961
7962     /**
7963      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7964      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7965      * process that block using the passed callback.
7966      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7967      * for the request to the remote server.
7968      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7969      * object into a block of Roo.data.Records.
7970      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7971      * The function must be passed <ul>
7972      * <li>The Record block object</li>
7973      * <li>The "arg" argument from the load function</li>
7974      * <li>A boolean success indicator</li>
7975      * </ul>
7976      * @param {Object} scope The scope in which to call the callback
7977      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7978      */
7979     load : function(params, reader, callback, scope, arg){
7980         if(this.fireEvent("beforeload", this, params) !== false){
7981             var  o = {
7982                 params : params || {},
7983                 request: {
7984                     callback : callback,
7985                     scope : scope,
7986                     arg : arg
7987                 },
7988                 reader: reader,
7989                 callback : this.loadResponse,
7990                 scope: this
7991             };
7992             if(this.useAjax){
7993                 Roo.applyIf(o, this.conn);
7994                 if(this.activeRequest){
7995                     Roo.Ajax.abort(this.activeRequest);
7996                 }
7997                 this.activeRequest = Roo.Ajax.request(o);
7998             }else{
7999                 this.conn.request(o);
8000             }
8001         }else{
8002             callback.call(scope||this, null, arg, false);
8003         }
8004     },
8005
8006     // private
8007     loadResponse : function(o, success, response){
8008         delete this.activeRequest;
8009         if(!success){
8010             this.fireEvent("loadexception", this, o, response);
8011             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8012             return;
8013         }
8014         var result;
8015         try {
8016             result = o.reader.read(response);
8017         }catch(e){
8018             this.fireEvent("loadexception", this, o, response, e);
8019             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8020             return;
8021         }
8022         
8023         this.fireEvent("load", this, o, o.request.arg);
8024         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8025     },
8026
8027     // private
8028     update : function(dataSet){
8029
8030     },
8031
8032     // private
8033     updateResponse : function(dataSet){
8034
8035     }
8036 });/*
8037  * Based on:
8038  * Ext JS Library 1.1.1
8039  * Copyright(c) 2006-2007, Ext JS, LLC.
8040  *
8041  * Originally Released Under LGPL - original licence link has changed is not relivant.
8042  *
8043  * Fork - LGPL
8044  * <script type="text/javascript">
8045  */
8046
8047 /**
8048  * @class Roo.data.ScriptTagProxy
8049  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8050  * other than the originating domain of the running page.<br><br>
8051  * <p>
8052  * <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
8053  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8054  * <p>
8055  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8056  * source code that is used as the source inside a &lt;script> tag.<br><br>
8057  * <p>
8058  * In order for the browser to process the returned data, the server must wrap the data object
8059  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8060  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8061  * depending on whether the callback name was passed:
8062  * <p>
8063  * <pre><code>
8064 boolean scriptTag = false;
8065 String cb = request.getParameter("callback");
8066 if (cb != null) {
8067     scriptTag = true;
8068     response.setContentType("text/javascript");
8069 } else {
8070     response.setContentType("application/x-json");
8071 }
8072 Writer out = response.getWriter();
8073 if (scriptTag) {
8074     out.write(cb + "(");
8075 }
8076 out.print(dataBlock.toJsonString());
8077 if (scriptTag) {
8078     out.write(");");
8079 }
8080 </pre></code>
8081  *
8082  * @constructor
8083  * @param {Object} config A configuration object.
8084  */
8085 Roo.data.ScriptTagProxy = function(config){
8086     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8087     Roo.apply(this, config);
8088     this.head = document.getElementsByTagName("head")[0];
8089 };
8090
8091 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8092
8093 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8094     /**
8095      * @cfg {String} url The URL from which to request the data object.
8096      */
8097     /**
8098      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8099      */
8100     timeout : 30000,
8101     /**
8102      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8103      * the server the name of the callback function set up by the load call to process the returned data object.
8104      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8105      * javascript output which calls this named function passing the data object as its only parameter.
8106      */
8107     callbackParam : "callback",
8108     /**
8109      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8110      * name to the request.
8111      */
8112     nocache : true,
8113
8114     /**
8115      * Load data from the configured URL, read the data object into
8116      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8117      * process that block using the passed callback.
8118      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8119      * for the request to the remote server.
8120      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8121      * object into a block of Roo.data.Records.
8122      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8123      * The function must be passed <ul>
8124      * <li>The Record block object</li>
8125      * <li>The "arg" argument from the load function</li>
8126      * <li>A boolean success indicator</li>
8127      * </ul>
8128      * @param {Object} scope The scope in which to call the callback
8129      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8130      */
8131     load : function(params, reader, callback, scope, arg){
8132         if(this.fireEvent("beforeload", this, params) !== false){
8133
8134             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8135
8136             var url = this.url;
8137             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8138             if(this.nocache){
8139                 url += "&_dc=" + (new Date().getTime());
8140             }
8141             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8142             var trans = {
8143                 id : transId,
8144                 cb : "stcCallback"+transId,
8145                 scriptId : "stcScript"+transId,
8146                 params : params,
8147                 arg : arg,
8148                 url : url,
8149                 callback : callback,
8150                 scope : scope,
8151                 reader : reader
8152             };
8153             var conn = this;
8154
8155             window[trans.cb] = function(o){
8156                 conn.handleResponse(o, trans);
8157             };
8158
8159             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8160
8161             if(this.autoAbort !== false){
8162                 this.abort();
8163             }
8164
8165             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8166
8167             var script = document.createElement("script");
8168             script.setAttribute("src", url);
8169             script.setAttribute("type", "text/javascript");
8170             script.setAttribute("id", trans.scriptId);
8171             this.head.appendChild(script);
8172
8173             this.trans = trans;
8174         }else{
8175             callback.call(scope||this, null, arg, false);
8176         }
8177     },
8178
8179     // private
8180     isLoading : function(){
8181         return this.trans ? true : false;
8182     },
8183
8184     /**
8185      * Abort the current server request.
8186      */
8187     abort : function(){
8188         if(this.isLoading()){
8189             this.destroyTrans(this.trans);
8190         }
8191     },
8192
8193     // private
8194     destroyTrans : function(trans, isLoaded){
8195         this.head.removeChild(document.getElementById(trans.scriptId));
8196         clearTimeout(trans.timeoutId);
8197         if(isLoaded){
8198             window[trans.cb] = undefined;
8199             try{
8200                 delete window[trans.cb];
8201             }catch(e){}
8202         }else{
8203             // if hasn't been loaded, wait for load to remove it to prevent script error
8204             window[trans.cb] = function(){
8205                 window[trans.cb] = undefined;
8206                 try{
8207                     delete window[trans.cb];
8208                 }catch(e){}
8209             };
8210         }
8211     },
8212
8213     // private
8214     handleResponse : function(o, trans){
8215         this.trans = false;
8216         this.destroyTrans(trans, true);
8217         var result;
8218         try {
8219             result = trans.reader.readRecords(o);
8220         }catch(e){
8221             this.fireEvent("loadexception", this, o, trans.arg, e);
8222             trans.callback.call(trans.scope||window, null, trans.arg, false);
8223             return;
8224         }
8225         this.fireEvent("load", this, o, trans.arg);
8226         trans.callback.call(trans.scope||window, result, trans.arg, true);
8227     },
8228
8229     // private
8230     handleFailure : function(trans){
8231         this.trans = false;
8232         this.destroyTrans(trans, false);
8233         this.fireEvent("loadexception", this, null, trans.arg);
8234         trans.callback.call(trans.scope||window, null, trans.arg, false);
8235     }
8236 });/*
8237  * Based on:
8238  * Ext JS Library 1.1.1
8239  * Copyright(c) 2006-2007, Ext JS, LLC.
8240  *
8241  * Originally Released Under LGPL - original licence link has changed is not relivant.
8242  *
8243  * Fork - LGPL
8244  * <script type="text/javascript">
8245  */
8246
8247 /**
8248  * @class Roo.data.JsonReader
8249  * @extends Roo.data.DataReader
8250  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8251  * based on mappings in a provided Roo.data.Record constructor.
8252  * 
8253  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8254  * in the reply previously. 
8255  * 
8256  * <p>
8257  * Example code:
8258  * <pre><code>
8259 var RecordDef = Roo.data.Record.create([
8260     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8261     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8262 ]);
8263 var myReader = new Roo.data.JsonReader({
8264     totalProperty: "results",    // The property which contains the total dataset size (optional)
8265     root: "rows",                // The property which contains an Array of row objects
8266     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8267 }, RecordDef);
8268 </code></pre>
8269  * <p>
8270  * This would consume a JSON file like this:
8271  * <pre><code>
8272 { 'results': 2, 'rows': [
8273     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8274     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8275 }
8276 </code></pre>
8277  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8278  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8279  * paged from the remote server.
8280  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8281  * @cfg {String} root name of the property which contains the Array of row objects.
8282  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8283  * @constructor
8284  * Create a new JsonReader
8285  * @param {Object} meta Metadata configuration options
8286  * @param {Object} recordType Either an Array of field definition objects,
8287  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8288  */
8289 Roo.data.JsonReader = function(meta, recordType){
8290     
8291     meta = meta || {};
8292     // set some defaults:
8293     Roo.applyIf(meta, {
8294         totalProperty: 'total',
8295         successProperty : 'success',
8296         root : 'data',
8297         id : 'id'
8298     });
8299     
8300     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8301 };
8302 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8303     
8304     /**
8305      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8306      * Used by Store query builder to append _requestMeta to params.
8307      * 
8308      */
8309     metaFromRemote : false,
8310     /**
8311      * This method is only used by a DataProxy which has retrieved data from a remote server.
8312      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8313      * @return {Object} data A data block which is used by an Roo.data.Store object as
8314      * a cache of Roo.data.Records.
8315      */
8316     read : function(response){
8317         var json = response.responseText;
8318        
8319         var o = /* eval:var:o */ eval("("+json+")");
8320         if(!o) {
8321             throw {message: "JsonReader.read: Json object not found"};
8322         }
8323         
8324         if(o.metaData){
8325             
8326             delete this.ef;
8327             this.metaFromRemote = true;
8328             this.meta = o.metaData;
8329             this.recordType = Roo.data.Record.create(o.metaData.fields);
8330             this.onMetaChange(this.meta, this.recordType, o);
8331         }
8332         return this.readRecords(o);
8333     },
8334
8335     // private function a store will implement
8336     onMetaChange : function(meta, recordType, o){
8337
8338     },
8339
8340     /**
8341          * @ignore
8342          */
8343     simpleAccess: function(obj, subsc) {
8344         return obj[subsc];
8345     },
8346
8347         /**
8348          * @ignore
8349          */
8350     getJsonAccessor: function(){
8351         var re = /[\[\.]/;
8352         return function(expr) {
8353             try {
8354                 return(re.test(expr))
8355                     ? new Function("obj", "return obj." + expr)
8356                     : function(obj){
8357                         return obj[expr];
8358                     };
8359             } catch(e){}
8360             return Roo.emptyFn;
8361         };
8362     }(),
8363
8364     /**
8365      * Create a data block containing Roo.data.Records from an XML document.
8366      * @param {Object} o An object which contains an Array of row objects in the property specified
8367      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8368      * which contains the total size of the dataset.
8369      * @return {Object} data A data block which is used by an Roo.data.Store object as
8370      * a cache of Roo.data.Records.
8371      */
8372     readRecords : function(o){
8373         /**
8374          * After any data loads, the raw JSON data is available for further custom processing.
8375          * @type Object
8376          */
8377         this.o = o;
8378         var s = this.meta, Record = this.recordType,
8379             f = Record.prototype.fields, fi = f.items, fl = f.length;
8380
8381 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8382         if (!this.ef) {
8383             if(s.totalProperty) {
8384                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8385                 }
8386                 if(s.successProperty) {
8387                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8388                 }
8389                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8390                 if (s.id) {
8391                         var g = this.getJsonAccessor(s.id);
8392                         this.getId = function(rec) {
8393                                 var r = g(rec);
8394                                 return (r === undefined || r === "") ? null : r;
8395                         };
8396                 } else {
8397                         this.getId = function(){return null;};
8398                 }
8399             this.ef = [];
8400             for(var jj = 0; jj < fl; jj++){
8401                 f = fi[jj];
8402                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8403                 this.ef[jj] = this.getJsonAccessor(map);
8404             }
8405         }
8406
8407         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8408         if(s.totalProperty){
8409             var vt = parseInt(this.getTotal(o), 10);
8410             if(!isNaN(vt)){
8411                 totalRecords = vt;
8412             }
8413         }
8414         if(s.successProperty){
8415             var vs = this.getSuccess(o);
8416             if(vs === false || vs === 'false'){
8417                 success = false;
8418             }
8419         }
8420         var records = [];
8421             for(var i = 0; i < c; i++){
8422                     var n = root[i];
8423                 var values = {};
8424                 var id = this.getId(n);
8425                 for(var j = 0; j < fl; j++){
8426                     f = fi[j];
8427                 var v = this.ef[j](n);
8428                 if (!f.convert) {
8429                     Roo.log('missing convert for ' + f.name);
8430                     Roo.log(f);
8431                     continue;
8432                 }
8433                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8434                 }
8435                 var record = new Record(values, id);
8436                 record.json = n;
8437                 records[i] = record;
8438             }
8439             return {
8440             raw : o,
8441                 success : success,
8442                 records : records,
8443                 totalRecords : totalRecords
8444             };
8445     }
8446 });/*
8447  * Based on:
8448  * Ext JS Library 1.1.1
8449  * Copyright(c) 2006-2007, Ext JS, LLC.
8450  *
8451  * Originally Released Under LGPL - original licence link has changed is not relivant.
8452  *
8453  * Fork - LGPL
8454  * <script type="text/javascript">
8455  */
8456
8457 /**
8458  * @class Roo.data.ArrayReader
8459  * @extends Roo.data.DataReader
8460  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8461  * Each element of that Array represents a row of data fields. The
8462  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8463  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8464  * <p>
8465  * Example code:.
8466  * <pre><code>
8467 var RecordDef = Roo.data.Record.create([
8468     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8469     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8470 ]);
8471 var myReader = new Roo.data.ArrayReader({
8472     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8473 }, RecordDef);
8474 </code></pre>
8475  * <p>
8476  * This would consume an Array like this:
8477  * <pre><code>
8478 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8479   </code></pre>
8480  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8481  * @constructor
8482  * Create a new JsonReader
8483  * @param {Object} meta Metadata configuration options.
8484  * @param {Object} recordType Either an Array of field definition objects
8485  * as specified to {@link Roo.data.Record#create},
8486  * or an {@link Roo.data.Record} object
8487  * created using {@link Roo.data.Record#create}.
8488  */
8489 Roo.data.ArrayReader = function(meta, recordType){
8490     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8491 };
8492
8493 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8494     /**
8495      * Create a data block containing Roo.data.Records from an XML document.
8496      * @param {Object} o An Array of row objects which represents the dataset.
8497      * @return {Object} data A data block which is used by an Roo.data.Store object as
8498      * a cache of Roo.data.Records.
8499      */
8500     readRecords : function(o){
8501         var sid = this.meta ? this.meta.id : null;
8502         var recordType = this.recordType, fields = recordType.prototype.fields;
8503         var records = [];
8504         var root = o;
8505             for(var i = 0; i < root.length; i++){
8506                     var n = root[i];
8507                 var values = {};
8508                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8509                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8510                 var f = fields.items[j];
8511                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8512                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8513                 v = f.convert(v);
8514                 values[f.name] = v;
8515             }
8516                 var record = new recordType(values, id);
8517                 record.json = n;
8518                 records[records.length] = record;
8519             }
8520             return {
8521                 records : records,
8522                 totalRecords : records.length
8523             };
8524     }
8525 });/*
8526  * - LGPL
8527  * * 
8528  */
8529
8530 /**
8531  * @class Roo.bootstrap.ComboBox
8532  * @extends Roo.bootstrap.TriggerField
8533  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8534  * @cfg {Boolean} append (true|false) default false
8535  * @constructor
8536  * Create a new ComboBox.
8537  * @param {Object} config Configuration options
8538  */
8539 Roo.bootstrap.ComboBox = function(config){
8540     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8541     this.addEvents({
8542         /**
8543          * @event expand
8544          * Fires when the dropdown list is expanded
8545              * @param {Roo.bootstrap.ComboBox} combo This combo box
8546              */
8547         'expand' : true,
8548         /**
8549          * @event collapse
8550          * Fires when the dropdown list is collapsed
8551              * @param {Roo.bootstrap.ComboBox} combo This combo box
8552              */
8553         'collapse' : true,
8554         /**
8555          * @event beforeselect
8556          * Fires before a list item is selected. Return false to cancel the selection.
8557              * @param {Roo.bootstrap.ComboBox} combo This combo box
8558              * @param {Roo.data.Record} record The data record returned from the underlying store
8559              * @param {Number} index The index of the selected item in the dropdown list
8560              */
8561         'beforeselect' : true,
8562         /**
8563          * @event select
8564          * Fires when a list item is selected
8565              * @param {Roo.bootstrap.ComboBox} combo This combo box
8566              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8567              * @param {Number} index The index of the selected item in the dropdown list
8568              */
8569         'select' : true,
8570         /**
8571          * @event beforequery
8572          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8573          * The event object passed has these properties:
8574              * @param {Roo.bootstrap.ComboBox} combo This combo box
8575              * @param {String} query The query
8576              * @param {Boolean} forceAll true to force "all" query
8577              * @param {Boolean} cancel true to cancel the query
8578              * @param {Object} e The query event object
8579              */
8580         'beforequery': true,
8581          /**
8582          * @event add
8583          * Fires when the 'add' icon is pressed (add a listener to enable add button)
8584              * @param {Roo.bootstrap.ComboBox} combo This combo box
8585              */
8586         'add' : true,
8587         /**
8588          * @event edit
8589          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
8590              * @param {Roo.bootstrap.ComboBox} combo This combo box
8591              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
8592              */
8593         'edit' : true,
8594         /**
8595          * @event remove
8596          * Fires when the remove value from the combobox array
8597              * @param {Roo.bootstrap.ComboBox} combo This combo box
8598              */
8599         'remove' : true
8600         
8601     });
8602     
8603     
8604     this.selectedIndex = -1;
8605     if(this.mode == 'local'){
8606         if(config.queryDelay === undefined){
8607             this.queryDelay = 10;
8608         }
8609         if(config.minChars === undefined){
8610             this.minChars = 0;
8611         }
8612     }
8613 };
8614
8615 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
8616      
8617     /**
8618      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
8619      * rendering into an Roo.Editor, defaults to false)
8620      */
8621     /**
8622      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
8623      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
8624      */
8625     /**
8626      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
8627      */
8628     /**
8629      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
8630      * the dropdown list (defaults to undefined, with no header element)
8631      */
8632
8633      /**
8634      * @cfg {String/Roo.Template} tpl The template to use to render the output
8635      */
8636      
8637      /**
8638      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
8639      */
8640     listWidth: undefined,
8641     /**
8642      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8643      * mode = 'remote' or 'text' if mode = 'local')
8644      */
8645     displayField: undefined,
8646     /**
8647      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8648      * mode = 'remote' or 'value' if mode = 'local'). 
8649      * Note: use of a valueField requires the user make a selection
8650      * in order for a value to be mapped.
8651      */
8652     valueField: undefined,
8653     
8654     
8655     /**
8656      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8657      * field's data value (defaults to the underlying DOM element's name)
8658      */
8659     hiddenName: undefined,
8660     /**
8661      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8662      */
8663     listClass: '',
8664     /**
8665      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8666      */
8667     selectedClass: 'active',
8668     
8669     /**
8670      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8671      */
8672     shadow:'sides',
8673     /**
8674      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8675      * anchor positions (defaults to 'tl-bl')
8676      */
8677     listAlign: 'tl-bl?',
8678     /**
8679      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8680      */
8681     maxHeight: 300,
8682     /**
8683      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
8684      * query specified by the allQuery config option (defaults to 'query')
8685      */
8686     triggerAction: 'query',
8687     /**
8688      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8689      * (defaults to 4, does not apply if editable = false)
8690      */
8691     minChars : 4,
8692     /**
8693      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8694      * delay (typeAheadDelay) if it matches a known value (defaults to false)
8695      */
8696     typeAhead: false,
8697     /**
8698      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8699      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8700      */
8701     queryDelay: 500,
8702     /**
8703      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8704      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
8705      */
8706     pageSize: 0,
8707     /**
8708      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
8709      * when editable = true (defaults to false)
8710      */
8711     selectOnFocus:false,
8712     /**
8713      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8714      */
8715     queryParam: 'query',
8716     /**
8717      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
8718      * when mode = 'remote' (defaults to 'Loading...')
8719      */
8720     loadingText: 'Loading...',
8721     /**
8722      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8723      */
8724     resizable: false,
8725     /**
8726      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8727      */
8728     handleHeight : 8,
8729     /**
8730      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8731      * traditional select (defaults to true)
8732      */
8733     editable: true,
8734     /**
8735      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8736      */
8737     allQuery: '',
8738     /**
8739      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8740      */
8741     mode: 'remote',
8742     /**
8743      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8744      * listWidth has a higher value)
8745      */
8746     minListWidth : 70,
8747     /**
8748      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8749      * allow the user to set arbitrary text into the field (defaults to false)
8750      */
8751     forceSelection:false,
8752     /**
8753      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8754      * if typeAhead = true (defaults to 250)
8755      */
8756     typeAheadDelay : 250,
8757     /**
8758      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8759      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8760      */
8761     valueNotFoundText : undefined,
8762     /**
8763      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8764      */
8765     blockFocus : false,
8766     
8767     /**
8768      * @cfg {Boolean} disableClear Disable showing of clear button.
8769      */
8770     disableClear : false,
8771     /**
8772      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8773      */
8774     alwaysQuery : false,
8775     
8776     /**
8777      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8778      */
8779     multiple : false,
8780     
8781     //private
8782     addicon : false,
8783     editicon: false,
8784     
8785     page: 0,
8786     hasQuery: false,
8787     append: false,
8788     loadNext: false,
8789     item: [],
8790     
8791     // element that contains real text value.. (when hidden is used..)
8792      
8793     // private
8794     initEvents: function(){
8795         
8796         if (!this.store) {
8797             throw "can not find store for combo";
8798         }
8799         this.store = Roo.factory(this.store, Roo.data);
8800         
8801         
8802         
8803         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8804         
8805         
8806         if(this.hiddenName){
8807             
8808             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8809             
8810             this.hiddenField.dom.value =
8811                 this.hiddenValue !== undefined ? this.hiddenValue :
8812                 this.value !== undefined ? this.value : '';
8813
8814             // prevent input submission
8815             this.el.dom.removeAttribute('name');
8816             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8817              
8818              
8819         }
8820         //if(Roo.isGecko){
8821         //    this.el.dom.setAttribute('autocomplete', 'off');
8822         //}
8823
8824         var cls = 'x-combo-list';
8825         this.list = this.el.select('ul.dropdown-menu',true).first();
8826
8827         //this.list = new Roo.Layer({
8828         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8829         //});
8830         
8831         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8832         this.list.setWidth(lw);
8833         
8834         this.list.on('mouseover', this.onViewOver, this);
8835         this.list.on('mousemove', this.onViewMove, this);
8836         
8837         this.list.on('scroll', this.onViewScroll, this);
8838         
8839         /*
8840         this.list.swallowEvent('mousewheel');
8841         this.assetHeight = 0;
8842
8843         if(this.title){
8844             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8845             this.assetHeight += this.header.getHeight();
8846         }
8847
8848         this.innerList = this.list.createChild({cls:cls+'-inner'});
8849         this.innerList.on('mouseover', this.onViewOver, this);
8850         this.innerList.on('mousemove', this.onViewMove, this);
8851         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8852         
8853         if(this.allowBlank && !this.pageSize && !this.disableClear){
8854             this.footer = this.list.createChild({cls:cls+'-ft'});
8855             this.pageTb = new Roo.Toolbar(this.footer);
8856            
8857         }
8858         if(this.pageSize){
8859             this.footer = this.list.createChild({cls:cls+'-ft'});
8860             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8861                     {pageSize: this.pageSize});
8862             
8863         }
8864         
8865         if (this.pageTb && this.allowBlank && !this.disableClear) {
8866             var _this = this;
8867             this.pageTb.add(new Roo.Toolbar.Fill(), {
8868                 cls: 'x-btn-icon x-btn-clear',
8869                 text: '&#160;',
8870                 handler: function()
8871                 {
8872                     _this.collapse();
8873                     _this.clearValue();
8874                     _this.onSelect(false, -1);
8875                 }
8876             });
8877         }
8878         if (this.footer) {
8879             this.assetHeight += this.footer.getHeight();
8880         }
8881         */
8882             
8883         if(!this.tpl){
8884             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8885         }
8886
8887         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8888             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8889         });
8890         //this.view.wrapEl.setDisplayed(false);
8891         this.view.on('click', this.onViewClick, this);
8892         
8893         
8894         
8895         this.store.on('beforeload', this.onBeforeLoad, this);
8896         this.store.on('load', this.onLoad, this);
8897         this.store.on('loadexception', this.onLoadException, this);
8898         /*
8899         if(this.resizable){
8900             this.resizer = new Roo.Resizable(this.list,  {
8901                pinned:true, handles:'se'
8902             });
8903             this.resizer.on('resize', function(r, w, h){
8904                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8905                 this.listWidth = w;
8906                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8907                 this.restrictHeight();
8908             }, this);
8909             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8910         }
8911         */
8912         if(!this.editable){
8913             this.editable = true;
8914             this.setEditable(false);
8915         }
8916         
8917         /*
8918         
8919         if (typeof(this.events.add.listeners) != 'undefined') {
8920             
8921             this.addicon = this.wrap.createChild(
8922                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8923        
8924             this.addicon.on('click', function(e) {
8925                 this.fireEvent('add', this);
8926             }, this);
8927         }
8928         if (typeof(this.events.edit.listeners) != 'undefined') {
8929             
8930             this.editicon = this.wrap.createChild(
8931                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8932             if (this.addicon) {
8933                 this.editicon.setStyle('margin-left', '40px');
8934             }
8935             this.editicon.on('click', function(e) {
8936                 
8937                 // we fire even  if inothing is selected..
8938                 this.fireEvent('edit', this, this.lastData );
8939                 
8940             }, this);
8941         }
8942         */
8943         
8944         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8945             "up" : function(e){
8946                 this.inKeyMode = true;
8947                 this.selectPrev();
8948             },
8949
8950             "down" : function(e){
8951                 if(!this.isExpanded()){
8952                     this.onTriggerClick();
8953                 }else{
8954                     this.inKeyMode = true;
8955                     this.selectNext();
8956                 }
8957             },
8958
8959             "enter" : function(e){
8960                 this.onViewClick();
8961                 //return true;
8962             },
8963
8964             "esc" : function(e){
8965                 this.collapse();
8966             },
8967
8968             "tab" : function(e){
8969                 this.collapse();
8970                 
8971                 if(this.fireEvent("specialkey", this, e)){
8972                     this.onViewClick(false);
8973                 }
8974                 
8975                 return true;
8976             },
8977
8978             scope : this,
8979
8980             doRelay : function(foo, bar, hname){
8981                 if(hname == 'down' || this.scope.isExpanded()){
8982                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8983                 }
8984                 return true;
8985             },
8986
8987             forceKeyDown: true
8988         });
8989         
8990         
8991         this.queryDelay = Math.max(this.queryDelay || 10,
8992                 this.mode == 'local' ? 10 : 250);
8993         
8994         
8995         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8996         
8997         if(this.typeAhead){
8998             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8999         }
9000         if(this.editable !== false){
9001             this.inputEl().on("keyup", this.onKeyUp, this);
9002         }
9003         if(this.forceSelection){
9004             this.on('blur', this.doForce, this);
9005         }
9006         
9007         if(this.multiple){
9008             this.choices = this.el.select('ul.select2-choices', true).first();
9009             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9010         }
9011     },
9012
9013     onDestroy : function(){
9014         if(this.view){
9015             this.view.setStore(null);
9016             this.view.el.removeAllListeners();
9017             this.view.el.remove();
9018             this.view.purgeListeners();
9019         }
9020         if(this.list){
9021             this.list.dom.innerHTML  = '';
9022         }
9023         if(this.store){
9024             this.store.un('beforeload', this.onBeforeLoad, this);
9025             this.store.un('load', this.onLoad, this);
9026             this.store.un('loadexception', this.onLoadException, this);
9027         }
9028         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9029     },
9030
9031     // private
9032     fireKey : function(e){
9033         if(e.isNavKeyPress() && !this.list.isVisible()){
9034             this.fireEvent("specialkey", this, e);
9035         }
9036     },
9037
9038     // private
9039     onResize: function(w, h){
9040 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9041 //        
9042 //        if(typeof w != 'number'){
9043 //            // we do not handle it!?!?
9044 //            return;
9045 //        }
9046 //        var tw = this.trigger.getWidth();
9047 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9048 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9049 //        var x = w - tw;
9050 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9051 //            
9052 //        //this.trigger.setStyle('left', x+'px');
9053 //        
9054 //        if(this.list && this.listWidth === undefined){
9055 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9056 //            this.list.setWidth(lw);
9057 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9058 //        }
9059         
9060     
9061         
9062     },
9063
9064     /**
9065      * Allow or prevent the user from directly editing the field text.  If false is passed,
9066      * the user will only be able to select from the items defined in the dropdown list.  This method
9067      * is the runtime equivalent of setting the 'editable' config option at config time.
9068      * @param {Boolean} value True to allow the user to directly edit the field text
9069      */
9070     setEditable : function(value){
9071         if(value == this.editable){
9072             return;
9073         }
9074         this.editable = value;
9075         if(!value){
9076             this.inputEl().dom.setAttribute('readOnly', true);
9077             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9078             this.inputEl().addClass('x-combo-noedit');
9079         }else{
9080             this.inputEl().dom.setAttribute('readOnly', false);
9081             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9082             this.inputEl().removeClass('x-combo-noedit');
9083         }
9084     },
9085
9086     // private
9087     
9088     onBeforeLoad : function(combo,opts){
9089         if(!this.hasFocus){
9090             return;
9091         }
9092          if (!opts.add) {
9093             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9094          }
9095         this.restrictHeight();
9096         this.selectedIndex = -1;
9097     },
9098
9099     // private
9100     onLoad : function(){
9101         
9102         this.hasQuery = false;
9103         
9104         if(!this.hasFocus){
9105             return;
9106         }
9107         
9108         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9109             this.loading.hide();
9110         }
9111         
9112         if(this.store.getCount() > 0){
9113             this.expand();
9114             this.restrictHeight();
9115             if(this.lastQuery == this.allQuery){
9116                 if(this.editable){
9117                     this.inputEl().dom.select();
9118                 }
9119                 if(!this.selectByValue(this.value, true)){
9120                     this.select(0, true);
9121                 }
9122             }else{
9123                 this.selectNext();
9124                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9125                     this.taTask.delay(this.typeAheadDelay);
9126                 }
9127             }
9128         }else{
9129             this.onEmptyResults();
9130         }
9131         
9132         //this.el.focus();
9133     },
9134     // private
9135     onLoadException : function()
9136     {
9137         this.hasQuery = false;
9138         
9139         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9140             this.loading.hide();
9141         }
9142         
9143         this.collapse();
9144         Roo.log(this.store.reader.jsonData);
9145         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9146             // fixme
9147             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9148         }
9149         
9150         
9151     },
9152     // private
9153     onTypeAhead : function(){
9154         if(this.store.getCount() > 0){
9155             var r = this.store.getAt(0);
9156             var newValue = r.data[this.displayField];
9157             var len = newValue.length;
9158             var selStart = this.getRawValue().length;
9159             
9160             if(selStart != len){
9161                 this.setRawValue(newValue);
9162                 this.selectText(selStart, newValue.length);
9163             }
9164         }
9165     },
9166
9167     // private
9168     onSelect : function(record, index){
9169         
9170         if(this.fireEvent('beforeselect', this, record, index) !== false){
9171         
9172             this.setFromData(index > -1 ? record.data : false);
9173             
9174             this.collapse();
9175             this.fireEvent('select', this, record, index);
9176         }
9177     },
9178
9179     /**
9180      * Returns the currently selected field value or empty string if no value is set.
9181      * @return {String} value The selected value
9182      */
9183     getValue : function(){
9184         
9185         if(this.multiple){
9186             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9187         }
9188         
9189         if(this.valueField){
9190             return typeof this.value != 'undefined' ? this.value : '';
9191         }else{
9192             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9193         }
9194     },
9195
9196     /**
9197      * Clears any text/value currently set in the field
9198      */
9199     clearValue : function(){
9200         if(this.hiddenField){
9201             this.hiddenField.dom.value = '';
9202         }
9203         this.value = '';
9204         this.setRawValue('');
9205         this.lastSelectionText = '';
9206         
9207     },
9208
9209     /**
9210      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9211      * will be displayed in the field.  If the value does not match the data value of an existing item,
9212      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9213      * Otherwise the field will be blank (although the value will still be set).
9214      * @param {String} value The value to match
9215      */
9216     setValue : function(v){
9217         if(this.multiple){
9218             this.syncValue();
9219             return;
9220         }
9221         
9222         var text = v;
9223         if(this.valueField){
9224             var r = this.findRecord(this.valueField, v);
9225             if(r){
9226                 text = r.data[this.displayField];
9227             }else if(this.valueNotFoundText !== undefined){
9228                 text = this.valueNotFoundText;
9229             }
9230         }
9231         this.lastSelectionText = text;
9232         if(this.hiddenField){
9233             this.hiddenField.dom.value = v;
9234         }
9235         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9236         this.value = v;
9237     },
9238     /**
9239      * @property {Object} the last set data for the element
9240      */
9241     
9242     lastData : false,
9243     /**
9244      * Sets the value of the field based on a object which is related to the record format for the store.
9245      * @param {Object} value the value to set as. or false on reset?
9246      */
9247     setFromData : function(o){
9248         
9249         if(this.multiple){
9250             this.addItem(o);
9251             return;
9252         }
9253             
9254         var dv = ''; // display value
9255         var vv = ''; // value value..
9256         this.lastData = o;
9257         if (this.displayField) {
9258             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9259         } else {
9260             // this is an error condition!!!
9261             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9262         }
9263         
9264         if(this.valueField){
9265             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9266         }
9267         
9268         if(this.hiddenField){
9269             this.hiddenField.dom.value = vv;
9270             
9271             this.lastSelectionText = dv;
9272             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9273             this.value = vv;
9274             return;
9275         }
9276         // no hidden field.. - we store the value in 'value', but still display
9277         // display field!!!!
9278         this.lastSelectionText = dv;
9279         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9280         this.value = vv;
9281         
9282         
9283     },
9284     // private
9285     reset : function(){
9286         // overridden so that last data is reset..
9287         this.setValue(this.originalValue);
9288         this.clearInvalid();
9289         this.lastData = false;
9290         if (this.view) {
9291             this.view.clearSelections();
9292         }
9293     },
9294     // private
9295     findRecord : function(prop, value){
9296         var record;
9297         if(this.store.getCount() > 0){
9298             this.store.each(function(r){
9299                 if(r.data[prop] == value){
9300                     record = r;
9301                     return false;
9302                 }
9303                 return true;
9304             });
9305         }
9306         return record;
9307     },
9308     
9309     getName: function()
9310     {
9311         // returns hidden if it's set..
9312         if (!this.rendered) {return ''};
9313         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9314         
9315     },
9316     // private
9317     onViewMove : function(e, t){
9318         this.inKeyMode = false;
9319     },
9320
9321     // private
9322     onViewOver : function(e, t){
9323         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9324             return;
9325         }
9326         var item = this.view.findItemFromChild(t);
9327         if(item){
9328             var index = this.view.indexOf(item);
9329             this.select(index, false);
9330         }
9331     },
9332
9333     // private
9334     onViewClick : function(doFocus)
9335     {
9336         var index = this.view.getSelectedIndexes()[0];
9337         var r = this.store.getAt(index);
9338         if(r){
9339             this.onSelect(r, index);
9340         }
9341         if(doFocus !== false && !this.blockFocus){
9342             this.inputEl().focus();
9343         }
9344     },
9345
9346     // private
9347     restrictHeight : function(){
9348         //this.innerList.dom.style.height = '';
9349         //var inner = this.innerList.dom;
9350         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9351         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9352         //this.list.beginUpdate();
9353         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9354         this.list.alignTo(this.inputEl(), this.listAlign);
9355         //this.list.endUpdate();
9356     },
9357
9358     // private
9359     onEmptyResults : function(){
9360         this.collapse();
9361     },
9362
9363     /**
9364      * Returns true if the dropdown list is expanded, else false.
9365      */
9366     isExpanded : function(){
9367         return this.list.isVisible();
9368     },
9369
9370     /**
9371      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9372      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9373      * @param {String} value The data value of the item to select
9374      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9375      * selected item if it is not currently in view (defaults to true)
9376      * @return {Boolean} True if the value matched an item in the list, else false
9377      */
9378     selectByValue : function(v, scrollIntoView){
9379         if(v !== undefined && v !== null){
9380             var r = this.findRecord(this.valueField || this.displayField, v);
9381             if(r){
9382                 this.select(this.store.indexOf(r), scrollIntoView);
9383                 return true;
9384             }
9385         }
9386         return false;
9387     },
9388
9389     /**
9390      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9391      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9392      * @param {Number} index The zero-based index of the list item to select
9393      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9394      * selected item if it is not currently in view (defaults to true)
9395      */
9396     select : function(index, scrollIntoView){
9397         this.selectedIndex = index;
9398         this.view.select(index);
9399         if(scrollIntoView !== false){
9400             var el = this.view.getNode(index);
9401             if(el){
9402                 //this.innerList.scrollChildIntoView(el, false);
9403                 
9404             }
9405         }
9406     },
9407
9408     // private
9409     selectNext : function(){
9410         var ct = this.store.getCount();
9411         if(ct > 0){
9412             if(this.selectedIndex == -1){
9413                 this.select(0);
9414             }else if(this.selectedIndex < ct-1){
9415                 this.select(this.selectedIndex+1);
9416             }
9417         }
9418     },
9419
9420     // private
9421     selectPrev : function(){
9422         var ct = this.store.getCount();
9423         if(ct > 0){
9424             if(this.selectedIndex == -1){
9425                 this.select(0);
9426             }else if(this.selectedIndex != 0){
9427                 this.select(this.selectedIndex-1);
9428             }
9429         }
9430     },
9431
9432     // private
9433     onKeyUp : function(e){
9434         if(this.editable !== false && !e.isSpecialKey()){
9435             this.lastKey = e.getKey();
9436             this.dqTask.delay(this.queryDelay);
9437         }
9438     },
9439
9440     // private
9441     validateBlur : function(){
9442         return !this.list || !this.list.isVisible();   
9443     },
9444
9445     // private
9446     initQuery : function(){
9447         this.doQuery(this.getRawValue());
9448     },
9449
9450     // private
9451     doForce : function(){
9452         if(this.el.dom.value.length > 0){
9453             this.el.dom.value =
9454                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9455              
9456         }
9457     },
9458
9459     /**
9460      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9461      * query allowing the query action to be canceled if needed.
9462      * @param {String} query The SQL query to execute
9463      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9464      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9465      * saved in the current store (defaults to false)
9466      */
9467     doQuery : function(q, forceAll){
9468         
9469         if(q === undefined || q === null){
9470             q = '';
9471         }
9472         var qe = {
9473             query: q,
9474             forceAll: forceAll,
9475             combo: this,
9476             cancel:false
9477         };
9478         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9479             return false;
9480         }
9481         q = qe.query;
9482         
9483         forceAll = qe.forceAll;
9484         if(forceAll === true || (q.length >= this.minChars)){
9485             
9486             this.hasQuery = true;
9487             
9488             if(this.lastQuery != q || this.alwaysQuery){
9489                 this.lastQuery = q;
9490                 if(this.mode == 'local'){
9491                     this.selectedIndex = -1;
9492                     if(forceAll){
9493                         this.store.clearFilter();
9494                     }else{
9495                         this.store.filter(this.displayField, q);
9496                     }
9497                     this.onLoad();
9498                 }else{
9499                     this.store.baseParams[this.queryParam] = q;
9500                     
9501                     var options = {params : this.getParams(q)};
9502                     
9503                     if(this.loadNext){
9504                         options.add = true;
9505                         options.params.start = this.page * this.pageSize;
9506                     }
9507                     
9508                     this.store.load(options);
9509                     this.expand();
9510                 }
9511             }else{
9512                 this.selectedIndex = -1;
9513                 this.onLoad();   
9514             }
9515         }
9516         
9517         this.loadNext = false;
9518     },
9519
9520     // private
9521     getParams : function(q){
9522         var p = {};
9523         //p[this.queryParam] = q;
9524         
9525         if(this.pageSize){
9526             p.start = 0;
9527             p.limit = this.pageSize;
9528         }
9529         return p;
9530     },
9531
9532     /**
9533      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9534      */
9535     collapse : function(){
9536         if(!this.isExpanded()){
9537             return;
9538         }
9539         
9540         this.list.hide();
9541         Roo.get(document).un('mousedown', this.collapseIf, this);
9542         Roo.get(document).un('mousewheel', this.collapseIf, this);
9543         if (!this.editable) {
9544             Roo.get(document).un('keydown', this.listKeyPress, this);
9545         }
9546         this.fireEvent('collapse', this);
9547     },
9548
9549     // private
9550     collapseIf : function(e){
9551         var in_combo  = e.within(this.el);
9552         var in_list =  e.within(this.list);
9553         
9554         if (in_combo || in_list) {
9555             //e.stopPropagation();
9556             return;
9557         }
9558
9559         this.collapse();
9560         
9561     },
9562
9563     /**
9564      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9565      */
9566     expand : function(){
9567        
9568         if(this.isExpanded() || !this.hasFocus){
9569             return;
9570         }
9571          Roo.log('expand');
9572         this.list.alignTo(this.inputEl(), this.listAlign);
9573         this.list.show();
9574         Roo.get(document).on('mousedown', this.collapseIf, this);
9575         Roo.get(document).on('mousewheel', this.collapseIf, this);
9576         if (!this.editable) {
9577             Roo.get(document).on('keydown', this.listKeyPress, this);
9578         }
9579         
9580         this.fireEvent('expand', this);
9581     },
9582
9583     // private
9584     // Implements the default empty TriggerField.onTriggerClick function
9585     onTriggerClick : function()
9586     {
9587         Roo.log('trigger click');
9588         
9589         if(this.disabled){
9590             return;
9591         }
9592         
9593         this.page = 0;
9594         this.loadNext = false;
9595         
9596         if(this.isExpanded()){
9597             this.collapse();
9598             if (!this.blockFocus) {
9599                 this.inputEl().focus();
9600             }
9601             
9602         }else {
9603             this.hasFocus = true;
9604             if(this.triggerAction == 'all') {
9605                 this.doQuery(this.allQuery, true);
9606             } else {
9607                 this.doQuery(this.getRawValue());
9608             }
9609             if (!this.blockFocus) {
9610                 this.inputEl().focus();
9611             }
9612         }
9613     },
9614     listKeyPress : function(e)
9615     {
9616         //Roo.log('listkeypress');
9617         // scroll to first matching element based on key pres..
9618         if (e.isSpecialKey()) {
9619             return false;
9620         }
9621         var k = String.fromCharCode(e.getKey()).toUpperCase();
9622         //Roo.log(k);
9623         var match  = false;
9624         var csel = this.view.getSelectedNodes();
9625         var cselitem = false;
9626         if (csel.length) {
9627             var ix = this.view.indexOf(csel[0]);
9628             cselitem  = this.store.getAt(ix);
9629             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
9630                 cselitem = false;
9631             }
9632             
9633         }
9634         
9635         this.store.each(function(v) { 
9636             if (cselitem) {
9637                 // start at existing selection.
9638                 if (cselitem.id == v.id) {
9639                     cselitem = false;
9640                 }
9641                 return true;
9642             }
9643                 
9644             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9645                 match = this.store.indexOf(v);
9646                 return false;
9647             }
9648             return true;
9649         }, this);
9650         
9651         if (match === false) {
9652             return true; // no more action?
9653         }
9654         // scroll to?
9655         this.view.select(match);
9656         var sn = Roo.get(this.view.getSelectedNodes()[0])
9657         //sn.scrollIntoView(sn.dom.parentNode, false);
9658     },
9659     
9660     onViewScroll : function(e, t){
9661         
9662         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9663             return;
9664         }
9665         
9666         this.hasQuery = true;
9667         
9668         this.loading = this.list.select('.loading', true).first();
9669         
9670         if(this.loading === null){
9671             this.list.createChild({
9672                 tag: 'div',
9673                 cls: 'loading select2-more-results select2-active',
9674                 html: 'Loading more results...'
9675             })
9676             
9677             this.loading = this.list.select('.loading', true).first();
9678             
9679             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9680             
9681             this.loading.hide();
9682         }
9683         
9684         this.loading.show();
9685         
9686         var _combo = this;
9687         
9688         this.page++;
9689         this.loadNext = true;
9690         
9691         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9692         
9693         return;
9694     },
9695     
9696     addItem : function(o)
9697     {   
9698         var dv = ''; // display value
9699         
9700         if (this.displayField) {
9701             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9702         } else {
9703             // this is an error condition!!!
9704             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9705         }
9706         
9707         if(!dv.length){
9708             return;
9709         }
9710         
9711         var choice = this.choices.createChild({
9712             tag: 'li',
9713             cls: 'select2-search-choice',
9714             cn: [
9715                 {
9716                     tag: 'div',
9717                     html: dv
9718                 },
9719                 {
9720                     tag: 'a',
9721                     href: '#',
9722                     cls: 'select2-search-choice-close',
9723                     tabindex: '-1'
9724                 }
9725             ]
9726             
9727         }, this.searchField);
9728         
9729         var close = choice.select('a.select2-search-choice-close', true).first()
9730         
9731         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9732         
9733         this.item.push(o);
9734         this.lastData = o;
9735         
9736         this.syncValue();
9737         
9738         this.inputEl().dom.value = '';
9739         
9740     },
9741     
9742     onRemoveItem : function(e, _self, o)
9743     {
9744         Roo.log('remove item');
9745         var index = this.item.indexOf(o.data) * 1;
9746         
9747         if( index < 0){
9748             Roo.log('not this item?!');
9749             return;
9750         }
9751         
9752         this.item.splice(index, 1);
9753         o.item.remove();
9754         
9755         this.syncValue();
9756         
9757         this.fireEvent('remove', this);
9758         
9759     },
9760     
9761     syncValue : function()
9762     {
9763         if(!this.item.length){
9764             this.clearValue();
9765             return;
9766         }
9767             
9768         var value = [];
9769         var _this = this;
9770         Roo.each(this.item, function(i){
9771             if(_this.valueField){
9772                 value.push(i[_this.valueField]);
9773                 return;
9774             }
9775
9776             value.push(i);
9777         });
9778
9779         this.value = value.join(',');
9780
9781         if(this.hiddenField){
9782             this.hiddenField.dom.value = this.value;
9783         }
9784     },
9785     
9786     clearItem : function()
9787     {
9788         if(!this.multiple){
9789             return;
9790         }
9791         
9792         this.item = [];
9793         
9794         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9795            c.remove();
9796         });
9797         
9798         this.syncValue();
9799     }
9800     
9801     
9802
9803     /** 
9804     * @cfg {Boolean} grow 
9805     * @hide 
9806     */
9807     /** 
9808     * @cfg {Number} growMin 
9809     * @hide 
9810     */
9811     /** 
9812     * @cfg {Number} growMax 
9813     * @hide 
9814     */
9815     /**
9816      * @hide
9817      * @method autoSize
9818      */
9819 });
9820 /*
9821  * Based on:
9822  * Ext JS Library 1.1.1
9823  * Copyright(c) 2006-2007, Ext JS, LLC.
9824  *
9825  * Originally Released Under LGPL - original licence link has changed is not relivant.
9826  *
9827  * Fork - LGPL
9828  * <script type="text/javascript">
9829  */
9830
9831 /**
9832  * @class Roo.View
9833  * @extends Roo.util.Observable
9834  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9835  * This class also supports single and multi selection modes. <br>
9836  * Create a data model bound view:
9837  <pre><code>
9838  var store = new Roo.data.Store(...);
9839
9840  var view = new Roo.View({
9841     el : "my-element",
9842     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9843  
9844     singleSelect: true,
9845     selectedClass: "ydataview-selected",
9846     store: store
9847  });
9848
9849  // listen for node click?
9850  view.on("click", function(vw, index, node, e){
9851  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9852  });
9853
9854  // load XML data
9855  dataModel.load("foobar.xml");
9856  </code></pre>
9857  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9858  * <br><br>
9859  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9860  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9861  * 
9862  * Note: old style constructor is still suported (container, template, config)
9863  * 
9864  * @constructor
9865  * Create a new View
9866  * @param {Object} config The config object
9867  * 
9868  */
9869 Roo.View = function(config, depreciated_tpl, depreciated_config){
9870     
9871     if (typeof(depreciated_tpl) == 'undefined') {
9872         // new way.. - universal constructor.
9873         Roo.apply(this, config);
9874         this.el  = Roo.get(this.el);
9875     } else {
9876         // old format..
9877         this.el  = Roo.get(config);
9878         this.tpl = depreciated_tpl;
9879         Roo.apply(this, depreciated_config);
9880     }
9881     this.wrapEl  = this.el.wrap().wrap();
9882     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9883     
9884     
9885     if(typeof(this.tpl) == "string"){
9886         this.tpl = new Roo.Template(this.tpl);
9887     } else {
9888         // support xtype ctors..
9889         this.tpl = new Roo.factory(this.tpl, Roo);
9890     }
9891     
9892     
9893     this.tpl.compile();
9894    
9895   
9896     
9897      
9898     /** @private */
9899     this.addEvents({
9900         /**
9901          * @event beforeclick
9902          * Fires before a click is processed. Returns false to cancel the default action.
9903          * @param {Roo.View} this
9904          * @param {Number} index The index of the target node
9905          * @param {HTMLElement} node The target node
9906          * @param {Roo.EventObject} e The raw event object
9907          */
9908             "beforeclick" : true,
9909         /**
9910          * @event click
9911          * Fires when a template node is clicked.
9912          * @param {Roo.View} this
9913          * @param {Number} index The index of the target node
9914          * @param {HTMLElement} node The target node
9915          * @param {Roo.EventObject} e The raw event object
9916          */
9917             "click" : true,
9918         /**
9919          * @event dblclick
9920          * Fires when a template node is double clicked.
9921          * @param {Roo.View} this
9922          * @param {Number} index The index of the target node
9923          * @param {HTMLElement} node The target node
9924          * @param {Roo.EventObject} e The raw event object
9925          */
9926             "dblclick" : true,
9927         /**
9928          * @event contextmenu
9929          * Fires when a template node is right clicked.
9930          * @param {Roo.View} this
9931          * @param {Number} index The index of the target node
9932          * @param {HTMLElement} node The target node
9933          * @param {Roo.EventObject} e The raw event object
9934          */
9935             "contextmenu" : true,
9936         /**
9937          * @event selectionchange
9938          * Fires when the selected nodes change.
9939          * @param {Roo.View} this
9940          * @param {Array} selections Array of the selected nodes
9941          */
9942             "selectionchange" : true,
9943     
9944         /**
9945          * @event beforeselect
9946          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9947          * @param {Roo.View} this
9948          * @param {HTMLElement} node The node to be selected
9949          * @param {Array} selections Array of currently selected nodes
9950          */
9951             "beforeselect" : true,
9952         /**
9953          * @event preparedata
9954          * Fires on every row to render, to allow you to change the data.
9955          * @param {Roo.View} this
9956          * @param {Object} data to be rendered (change this)
9957          */
9958           "preparedata" : true
9959           
9960           
9961         });
9962
9963
9964
9965     this.el.on({
9966         "click": this.onClick,
9967         "dblclick": this.onDblClick,
9968         "contextmenu": this.onContextMenu,
9969         scope:this
9970     });
9971
9972     this.selections = [];
9973     this.nodes = [];
9974     this.cmp = new Roo.CompositeElementLite([]);
9975     if(this.store){
9976         this.store = Roo.factory(this.store, Roo.data);
9977         this.setStore(this.store, true);
9978     }
9979     
9980     if ( this.footer && this.footer.xtype) {
9981            
9982          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9983         
9984         this.footer.dataSource = this.store
9985         this.footer.container = fctr;
9986         this.footer = Roo.factory(this.footer, Roo);
9987         fctr.insertFirst(this.el);
9988         
9989         // this is a bit insane - as the paging toolbar seems to detach the el..
9990 //        dom.parentNode.parentNode.parentNode
9991          // they get detached?
9992     }
9993     
9994     
9995     Roo.View.superclass.constructor.call(this);
9996     
9997     
9998 };
9999
10000 Roo.extend(Roo.View, Roo.util.Observable, {
10001     
10002      /**
10003      * @cfg {Roo.data.Store} store Data store to load data from.
10004      */
10005     store : false,
10006     
10007     /**
10008      * @cfg {String|Roo.Element} el The container element.
10009      */
10010     el : '',
10011     
10012     /**
10013      * @cfg {String|Roo.Template} tpl The template used by this View 
10014      */
10015     tpl : false,
10016     /**
10017      * @cfg {String} dataName the named area of the template to use as the data area
10018      *                          Works with domtemplates roo-name="name"
10019      */
10020     dataName: false,
10021     /**
10022      * @cfg {String} selectedClass The css class to add to selected nodes
10023      */
10024     selectedClass : "x-view-selected",
10025      /**
10026      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10027      */
10028     emptyText : "",
10029     
10030     /**
10031      * @cfg {String} text to display on mask (default Loading)
10032      */
10033     mask : false,
10034     /**
10035      * @cfg {Boolean} multiSelect Allow multiple selection
10036      */
10037     multiSelect : false,
10038     /**
10039      * @cfg {Boolean} singleSelect Allow single selection
10040      */
10041     singleSelect:  false,
10042     
10043     /**
10044      * @cfg {Boolean} toggleSelect - selecting 
10045      */
10046     toggleSelect : false,
10047     
10048     /**
10049      * Returns the element this view is bound to.
10050      * @return {Roo.Element}
10051      */
10052     getEl : function(){
10053         return this.wrapEl;
10054     },
10055     
10056     
10057
10058     /**
10059      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10060      */
10061     refresh : function(){
10062         Roo.log('refresh');
10063         var t = this.tpl;
10064         
10065         // if we are using something like 'domtemplate', then
10066         // the what gets used is:
10067         // t.applySubtemplate(NAME, data, wrapping data..)
10068         // the outer template then get' applied with
10069         //     the store 'extra data'
10070         // and the body get's added to the
10071         //      roo-name="data" node?
10072         //      <span class='roo-tpl-{name}'></span> ?????
10073         
10074         
10075         
10076         this.clearSelections();
10077         this.el.update("");
10078         var html = [];
10079         var records = this.store.getRange();
10080         if(records.length < 1) {
10081             
10082             // is this valid??  = should it render a template??
10083             
10084             this.el.update(this.emptyText);
10085             return;
10086         }
10087         var el = this.el;
10088         if (this.dataName) {
10089             this.el.update(t.apply(this.store.meta)); //????
10090             el = this.el.child('.roo-tpl-' + this.dataName);
10091         }
10092         
10093         for(var i = 0, len = records.length; i < len; i++){
10094             var data = this.prepareData(records[i].data, i, records[i]);
10095             this.fireEvent("preparedata", this, data, i, records[i]);
10096             html[html.length] = Roo.util.Format.trim(
10097                 this.dataName ?
10098                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10099                     t.apply(data)
10100             );
10101         }
10102         
10103         
10104         
10105         el.update(html.join(""));
10106         this.nodes = el.dom.childNodes;
10107         this.updateIndexes(0);
10108     },
10109     
10110
10111     /**
10112      * Function to override to reformat the data that is sent to
10113      * the template for each node.
10114      * DEPRICATED - use the preparedata event handler.
10115      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10116      * a JSON object for an UpdateManager bound view).
10117      */
10118     prepareData : function(data, index, record)
10119     {
10120         this.fireEvent("preparedata", this, data, index, record);
10121         return data;
10122     },
10123
10124     onUpdate : function(ds, record){
10125          Roo.log('on update');   
10126         this.clearSelections();
10127         var index = this.store.indexOf(record);
10128         var n = this.nodes[index];
10129         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10130         n.parentNode.removeChild(n);
10131         this.updateIndexes(index, index);
10132     },
10133
10134     
10135     
10136 // --------- FIXME     
10137     onAdd : function(ds, records, index)
10138     {
10139         Roo.log(['on Add', ds, records, index] );        
10140         this.clearSelections();
10141         if(this.nodes.length == 0){
10142             this.refresh();
10143             return;
10144         }
10145         var n = this.nodes[index];
10146         for(var i = 0, len = records.length; i < len; i++){
10147             var d = this.prepareData(records[i].data, i, records[i]);
10148             if(n){
10149                 this.tpl.insertBefore(n, d);
10150             }else{
10151                 
10152                 this.tpl.append(this.el, d);
10153             }
10154         }
10155         this.updateIndexes(index);
10156     },
10157
10158     onRemove : function(ds, record, index){
10159         Roo.log('onRemove');
10160         this.clearSelections();
10161         var el = this.dataName  ?
10162             this.el.child('.roo-tpl-' + this.dataName) :
10163             this.el; 
10164         
10165         el.dom.removeChild(this.nodes[index]);
10166         this.updateIndexes(index);
10167     },
10168
10169     /**
10170      * Refresh an individual node.
10171      * @param {Number} index
10172      */
10173     refreshNode : function(index){
10174         this.onUpdate(this.store, this.store.getAt(index));
10175     },
10176
10177     updateIndexes : function(startIndex, endIndex){
10178         var ns = this.nodes;
10179         startIndex = startIndex || 0;
10180         endIndex = endIndex || ns.length - 1;
10181         for(var i = startIndex; i <= endIndex; i++){
10182             ns[i].nodeIndex = i;
10183         }
10184     },
10185
10186     /**
10187      * Changes the data store this view uses and refresh the view.
10188      * @param {Store} store
10189      */
10190     setStore : function(store, initial){
10191         if(!initial && this.store){
10192             this.store.un("datachanged", this.refresh);
10193             this.store.un("add", this.onAdd);
10194             this.store.un("remove", this.onRemove);
10195             this.store.un("update", this.onUpdate);
10196             this.store.un("clear", this.refresh);
10197             this.store.un("beforeload", this.onBeforeLoad);
10198             this.store.un("load", this.onLoad);
10199             this.store.un("loadexception", this.onLoad);
10200         }
10201         if(store){
10202           
10203             store.on("datachanged", this.refresh, this);
10204             store.on("add", this.onAdd, this);
10205             store.on("remove", this.onRemove, this);
10206             store.on("update", this.onUpdate, this);
10207             store.on("clear", this.refresh, this);
10208             store.on("beforeload", this.onBeforeLoad, this);
10209             store.on("load", this.onLoad, this);
10210             store.on("loadexception", this.onLoad, this);
10211         }
10212         
10213         if(store){
10214             this.refresh();
10215         }
10216     },
10217     /**
10218      * onbeforeLoad - masks the loading area.
10219      *
10220      */
10221     onBeforeLoad : function(store,opts)
10222     {
10223          Roo.log('onBeforeLoad');   
10224         if (!opts.add) {
10225             this.el.update("");
10226         }
10227         this.el.mask(this.mask ? this.mask : "Loading" ); 
10228     },
10229     onLoad : function ()
10230     {
10231         this.el.unmask();
10232     },
10233     
10234
10235     /**
10236      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10237      * @param {HTMLElement} node
10238      * @return {HTMLElement} The template node
10239      */
10240     findItemFromChild : function(node){
10241         var el = this.dataName  ?
10242             this.el.child('.roo-tpl-' + this.dataName,true) :
10243             this.el.dom; 
10244         
10245         if(!node || node.parentNode == el){
10246                     return node;
10247             }
10248             var p = node.parentNode;
10249             while(p && p != el){
10250             if(p.parentNode == el){
10251                 return p;
10252             }
10253             p = p.parentNode;
10254         }
10255             return null;
10256     },
10257
10258     /** @ignore */
10259     onClick : function(e){
10260         var item = this.findItemFromChild(e.getTarget());
10261         if(item){
10262             var index = this.indexOf(item);
10263             if(this.onItemClick(item, index, e) !== false){
10264                 this.fireEvent("click", this, index, item, e);
10265             }
10266         }else{
10267             this.clearSelections();
10268         }
10269     },
10270
10271     /** @ignore */
10272     onContextMenu : function(e){
10273         var item = this.findItemFromChild(e.getTarget());
10274         if(item){
10275             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10276         }
10277     },
10278
10279     /** @ignore */
10280     onDblClick : function(e){
10281         var item = this.findItemFromChild(e.getTarget());
10282         if(item){
10283             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10284         }
10285     },
10286
10287     onItemClick : function(item, index, e)
10288     {
10289         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10290             return false;
10291         }
10292         if (this.toggleSelect) {
10293             var m = this.isSelected(item) ? 'unselect' : 'select';
10294             Roo.log(m);
10295             var _t = this;
10296             _t[m](item, true, false);
10297             return true;
10298         }
10299         if(this.multiSelect || this.singleSelect){
10300             if(this.multiSelect && e.shiftKey && this.lastSelection){
10301                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10302             }else{
10303                 this.select(item, this.multiSelect && e.ctrlKey);
10304                 this.lastSelection = item;
10305             }
10306             e.preventDefault();
10307         }
10308         return true;
10309     },
10310
10311     /**
10312      * Get the number of selected nodes.
10313      * @return {Number}
10314      */
10315     getSelectionCount : function(){
10316         return this.selections.length;
10317     },
10318
10319     /**
10320      * Get the currently selected nodes.
10321      * @return {Array} An array of HTMLElements
10322      */
10323     getSelectedNodes : function(){
10324         return this.selections;
10325     },
10326
10327     /**
10328      * Get the indexes of the selected nodes.
10329      * @return {Array}
10330      */
10331     getSelectedIndexes : function(){
10332         var indexes = [], s = this.selections;
10333         for(var i = 0, len = s.length; i < len; i++){
10334             indexes.push(s[i].nodeIndex);
10335         }
10336         return indexes;
10337     },
10338
10339     /**
10340      * Clear all selections
10341      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10342      */
10343     clearSelections : function(suppressEvent){
10344         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10345             this.cmp.elements = this.selections;
10346             this.cmp.removeClass(this.selectedClass);
10347             this.selections = [];
10348             if(!suppressEvent){
10349                 this.fireEvent("selectionchange", this, this.selections);
10350             }
10351         }
10352     },
10353
10354     /**
10355      * Returns true if the passed node is selected
10356      * @param {HTMLElement/Number} node The node or node index
10357      * @return {Boolean}
10358      */
10359     isSelected : function(node){
10360         var s = this.selections;
10361         if(s.length < 1){
10362             return false;
10363         }
10364         node = this.getNode(node);
10365         return s.indexOf(node) !== -1;
10366     },
10367
10368     /**
10369      * Selects nodes.
10370      * @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
10371      * @param {Boolean} keepExisting (optional) true to keep existing selections
10372      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10373      */
10374     select : function(nodeInfo, keepExisting, suppressEvent){
10375         if(nodeInfo instanceof Array){
10376             if(!keepExisting){
10377                 this.clearSelections(true);
10378             }
10379             for(var i = 0, len = nodeInfo.length; i < len; i++){
10380                 this.select(nodeInfo[i], true, true);
10381             }
10382             return;
10383         } 
10384         var node = this.getNode(nodeInfo);
10385         if(!node || this.isSelected(node)){
10386             return; // already selected.
10387         }
10388         if(!keepExisting){
10389             this.clearSelections(true);
10390         }
10391         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10392             Roo.fly(node).addClass(this.selectedClass);
10393             this.selections.push(node);
10394             if(!suppressEvent){
10395                 this.fireEvent("selectionchange", this, this.selections);
10396             }
10397         }
10398         
10399         
10400     },
10401       /**
10402      * Unselects nodes.
10403      * @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
10404      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10405      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10406      */
10407     unselect : function(nodeInfo, keepExisting, suppressEvent)
10408     {
10409         if(nodeInfo instanceof Array){
10410             Roo.each(this.selections, function(s) {
10411                 this.unselect(s, nodeInfo);
10412             }, this);
10413             return;
10414         }
10415         var node = this.getNode(nodeInfo);
10416         if(!node || !this.isSelected(node)){
10417             Roo.log("not selected");
10418             return; // not selected.
10419         }
10420         // fireevent???
10421         var ns = [];
10422         Roo.each(this.selections, function(s) {
10423             if (s == node ) {
10424                 Roo.fly(node).removeClass(this.selectedClass);
10425
10426                 return;
10427             }
10428             ns.push(s);
10429         },this);
10430         
10431         this.selections= ns;
10432         this.fireEvent("selectionchange", this, this.selections);
10433     },
10434
10435     /**
10436      * Gets a template node.
10437      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10438      * @return {HTMLElement} The node or null if it wasn't found
10439      */
10440     getNode : function(nodeInfo){
10441         if(typeof nodeInfo == "string"){
10442             return document.getElementById(nodeInfo);
10443         }else if(typeof nodeInfo == "number"){
10444             return this.nodes[nodeInfo];
10445         }
10446         return nodeInfo;
10447     },
10448
10449     /**
10450      * Gets a range template nodes.
10451      * @param {Number} startIndex
10452      * @param {Number} endIndex
10453      * @return {Array} An array of nodes
10454      */
10455     getNodes : function(start, end){
10456         var ns = this.nodes;
10457         start = start || 0;
10458         end = typeof end == "undefined" ? ns.length - 1 : end;
10459         var nodes = [];
10460         if(start <= end){
10461             for(var i = start; i <= end; i++){
10462                 nodes.push(ns[i]);
10463             }
10464         } else{
10465             for(var i = start; i >= end; i--){
10466                 nodes.push(ns[i]);
10467             }
10468         }
10469         return nodes;
10470     },
10471
10472     /**
10473      * Finds the index of the passed node
10474      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10475      * @return {Number} The index of the node or -1
10476      */
10477     indexOf : function(node){
10478         node = this.getNode(node);
10479         if(typeof node.nodeIndex == "number"){
10480             return node.nodeIndex;
10481         }
10482         var ns = this.nodes;
10483         for(var i = 0, len = ns.length; i < len; i++){
10484             if(ns[i] == node){
10485                 return i;
10486             }
10487         }
10488         return -1;
10489     }
10490 });
10491 /*
10492  * - LGPL
10493  *
10494  * based on jquery fullcalendar
10495  * 
10496  */
10497
10498 Roo.bootstrap = Roo.bootstrap || {};
10499 /**
10500  * @class Roo.bootstrap.Calendar
10501  * @extends Roo.bootstrap.Component
10502  * Bootstrap Calendar class
10503  * @cfg {Boolean} loadMask (true|false) default false
10504  * @cfg {Object} header generate the user specific header of the calendar, default false
10505
10506  * @constructor
10507  * Create a new Container
10508  * @param {Object} config The config object
10509  */
10510
10511
10512
10513 Roo.bootstrap.Calendar = function(config){
10514     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10515      this.addEvents({
10516         /**
10517              * @event select
10518              * Fires when a date is selected
10519              * @param {DatePicker} this
10520              * @param {Date} date The selected date
10521              */
10522         'select': true,
10523         /**
10524              * @event monthchange
10525              * Fires when the displayed month changes 
10526              * @param {DatePicker} this
10527              * @param {Date} date The selected month
10528              */
10529         'monthchange': true,
10530         /**
10531              * @event evententer
10532              * Fires when mouse over an event
10533              * @param {Calendar} this
10534              * @param {event} Event
10535              */
10536         'evententer': true,
10537         /**
10538              * @event eventleave
10539              * Fires when the mouse leaves an
10540              * @param {Calendar} this
10541              * @param {event}
10542              */
10543         'eventleave': true,
10544         /**
10545              * @event eventclick
10546              * Fires when the mouse click an
10547              * @param {Calendar} this
10548              * @param {event}
10549              */
10550         'eventclick': true
10551         
10552     });
10553
10554 };
10555
10556 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10557     
10558      /**
10559      * @cfg {Number} startDay
10560      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10561      */
10562     startDay : 0,
10563     
10564     loadMask : false,
10565     
10566     header : false,
10567       
10568     getAutoCreate : function(){
10569         
10570         
10571         var fc_button = function(name, corner, style, content ) {
10572             return Roo.apply({},{
10573                 tag : 'span',
10574                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10575                          (corner.length ?
10576                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10577                             ''
10578                         ),
10579                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10580                 unselectable: 'on'
10581             });
10582         };
10583         
10584         var header = {};
10585         
10586         if(!this.header){
10587             header = {
10588                 tag : 'table',
10589                 cls : 'fc-header',
10590                 style : 'width:100%',
10591                 cn : [
10592                     {
10593                         tag: 'tr',
10594                         cn : [
10595                             {
10596                                 tag : 'td',
10597                                 cls : 'fc-header-left',
10598                                 cn : [
10599                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
10600                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
10601                                     { tag: 'span', cls: 'fc-header-space' },
10602                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
10603
10604
10605                                 ]
10606                             },
10607
10608                             {
10609                                 tag : 'td',
10610                                 cls : 'fc-header-center',
10611                                 cn : [
10612                                     {
10613                                         tag: 'span',
10614                                         cls: 'fc-header-title',
10615                                         cn : {
10616                                             tag: 'H2',
10617                                             html : 'month / year'
10618                                         }
10619                                     }
10620
10621                                 ]
10622                             },
10623                             {
10624                                 tag : 'td',
10625                                 cls : 'fc-header-right',
10626                                 cn : [
10627                               /*      fc_button('month', 'left', '', 'month' ),
10628                                     fc_button('week', '', '', 'week' ),
10629                                     fc_button('day', 'right', '', 'day' )
10630                                 */    
10631
10632                                 ]
10633                             }
10634
10635                         ]
10636                     }
10637                 ]
10638             };
10639         }
10640         
10641         header = this.header;
10642         
10643        
10644         var cal_heads = function() {
10645             var ret = [];
10646             // fixme - handle this.
10647             
10648             for (var i =0; i < Date.dayNames.length; i++) {
10649                 var d = Date.dayNames[i];
10650                 ret.push({
10651                     tag: 'th',
10652                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10653                     html : d.substring(0,3)
10654                 });
10655                 
10656             }
10657             ret[0].cls += ' fc-first';
10658             ret[6].cls += ' fc-last';
10659             return ret;
10660         };
10661         var cal_cell = function(n) {
10662             return  {
10663                 tag: 'td',
10664                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10665                 cn : [
10666                     {
10667                         cn : [
10668                             {
10669                                 cls: 'fc-day-number',
10670                                 html: 'D'
10671                             },
10672                             {
10673                                 cls: 'fc-day-content',
10674                              
10675                                 cn : [
10676                                      {
10677                                         style: 'position: relative;' // height: 17px;
10678                                     }
10679                                 ]
10680                             }
10681                             
10682                             
10683                         ]
10684                     }
10685                 ]
10686                 
10687             }
10688         };
10689         var cal_rows = function() {
10690             
10691             var ret = []
10692             for (var r = 0; r < 6; r++) {
10693                 var row= {
10694                     tag : 'tr',
10695                     cls : 'fc-week',
10696                     cn : []
10697                 };
10698                 
10699                 for (var i =0; i < Date.dayNames.length; i++) {
10700                     var d = Date.dayNames[i];
10701                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10702
10703                 }
10704                 row.cn[0].cls+=' fc-first';
10705                 row.cn[0].cn[0].style = 'min-height:90px';
10706                 row.cn[6].cls+=' fc-last';
10707                 ret.push(row);
10708                 
10709             }
10710             ret[0].cls += ' fc-first';
10711             ret[4].cls += ' fc-prev-last';
10712             ret[5].cls += ' fc-last';
10713             return ret;
10714             
10715         };
10716         
10717         var cal_table = {
10718             tag: 'table',
10719             cls: 'fc-border-separate',
10720             style : 'width:100%',
10721             cellspacing  : 0,
10722             cn : [
10723                 { 
10724                     tag: 'thead',
10725                     cn : [
10726                         { 
10727                             tag: 'tr',
10728                             cls : 'fc-first fc-last',
10729                             cn : cal_heads()
10730                         }
10731                     ]
10732                 },
10733                 { 
10734                     tag: 'tbody',
10735                     cn : cal_rows()
10736                 }
10737                   
10738             ]
10739         };
10740          
10741          var cfg = {
10742             cls : 'fc fc-ltr',
10743             cn : [
10744                 header,
10745                 {
10746                     cls : 'fc-content',
10747                     style : "position: relative;",
10748                     cn : [
10749                         {
10750                             cls : 'fc-view fc-view-month fc-grid',
10751                             style : 'position: relative',
10752                             unselectable : 'on',
10753                             cn : [
10754                                 {
10755                                     cls : 'fc-event-container',
10756                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10757                                 },
10758                                 cal_table
10759                             ]
10760                         }
10761                     ]
10762     
10763                 }
10764            ] 
10765             
10766         };
10767         
10768          
10769         
10770         return cfg;
10771     },
10772     
10773     
10774     initEvents : function()
10775     {
10776         if(!this.store){
10777             throw "can not find store for calendar";
10778         }
10779         
10780         var mark = {
10781             tag: "div",
10782             cls:"x-dlg-mask",
10783             style: "text-align:center",
10784             cn: [
10785                 {
10786                     tag: "div",
10787                     style: "background-color:white;width:50%;margin:250 auto",
10788                     cn: [
10789                         {
10790                             tag: "img",
10791                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10792                         },
10793                         {
10794                             tag: "span",
10795                             html: "Loading"
10796                         }
10797                         
10798                     ]
10799                 }
10800             ]
10801         }
10802         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10803         
10804         var size = this.el.select('.fc-content', true).first().getSize();
10805         this.maskEl.setSize(size.width, size.height);
10806         this.maskEl.enableDisplayMode("block");
10807         if(!this.loadMask){
10808             this.maskEl.hide();
10809         }
10810         
10811         this.store = Roo.factory(this.store, Roo.data);
10812         this.store.on('load', this.onLoad, this);
10813         this.store.on('beforeload', this.onBeforeLoad, this);
10814         
10815         this.resize();
10816         
10817         this.cells = this.el.select('.fc-day',true);
10818         //Roo.log(this.cells);
10819         this.textNodes = this.el.query('.fc-day-number');
10820         this.cells.addClassOnOver('fc-state-hover');
10821         
10822         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10823         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10824         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10825         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10826         
10827         this.on('monthchange', this.onMonthChange, this);
10828         
10829         this.update(new Date().clearTime());
10830     },
10831     
10832     resize : function() {
10833         var sz  = this.el.getSize();
10834         
10835         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10836         this.el.select('.fc-day-content div',true).setHeight(34);
10837     },
10838     
10839     
10840     // private
10841     showPrevMonth : function(e){
10842         this.update(this.activeDate.add("mo", -1));
10843     },
10844     showToday : function(e){
10845         this.update(new Date().clearTime());
10846     },
10847     // private
10848     showNextMonth : function(e){
10849         this.update(this.activeDate.add("mo", 1));
10850     },
10851
10852     // private
10853     showPrevYear : function(){
10854         this.update(this.activeDate.add("y", -1));
10855     },
10856
10857     // private
10858     showNextYear : function(){
10859         this.update(this.activeDate.add("y", 1));
10860     },
10861
10862     
10863    // private
10864     update : function(date)
10865     {
10866         var vd = this.activeDate;
10867         this.activeDate = date;
10868 //        if(vd && this.el){
10869 //            var t = date.getTime();
10870 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10871 //                Roo.log('using add remove');
10872 //                
10873 //                this.fireEvent('monthchange', this, date);
10874 //                
10875 //                this.cells.removeClass("fc-state-highlight");
10876 //                this.cells.each(function(c){
10877 //                   if(c.dateValue == t){
10878 //                       c.addClass("fc-state-highlight");
10879 //                       setTimeout(function(){
10880 //                            try{c.dom.firstChild.focus();}catch(e){}
10881 //                       }, 50);
10882 //                       return false;
10883 //                   }
10884 //                   return true;
10885 //                });
10886 //                return;
10887 //            }
10888 //        }
10889         
10890         var days = date.getDaysInMonth();
10891         
10892         var firstOfMonth = date.getFirstDateOfMonth();
10893         var startingPos = firstOfMonth.getDay()-this.startDay;
10894         
10895         if(startingPos < this.startDay){
10896             startingPos += 7;
10897         }
10898         
10899         var pm = date.add(Date.MONTH, -1);
10900         var prevStart = pm.getDaysInMonth()-startingPos;
10901 //        
10902         this.cells = this.el.select('.fc-day',true);
10903         this.textNodes = this.el.query('.fc-day-number');
10904         this.cells.addClassOnOver('fc-state-hover');
10905         
10906         var cells = this.cells.elements;
10907         var textEls = this.textNodes;
10908         
10909         Roo.each(cells, function(cell){
10910             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10911         });
10912         
10913         days += startingPos;
10914
10915         // convert everything to numbers so it's fast
10916         var day = 86400000;
10917         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10918         //Roo.log(d);
10919         //Roo.log(pm);
10920         //Roo.log(prevStart);
10921         
10922         var today = new Date().clearTime().getTime();
10923         var sel = date.clearTime().getTime();
10924         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10925         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10926         var ddMatch = this.disabledDatesRE;
10927         var ddText = this.disabledDatesText;
10928         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10929         var ddaysText = this.disabledDaysText;
10930         var format = this.format;
10931         
10932         var setCellClass = function(cal, cell){
10933             
10934             //Roo.log('set Cell Class');
10935             cell.title = "";
10936             var t = d.getTime();
10937             
10938             //Roo.log(d);
10939             
10940             cell.dateValue = t;
10941             if(t == today){
10942                 cell.className += " fc-today";
10943                 cell.className += " fc-state-highlight";
10944                 cell.title = cal.todayText;
10945             }
10946             if(t == sel){
10947                 // disable highlight in other month..
10948                 //cell.className += " fc-state-highlight";
10949                 
10950             }
10951             // disabling
10952             if(t < min) {
10953                 cell.className = " fc-state-disabled";
10954                 cell.title = cal.minText;
10955                 return;
10956             }
10957             if(t > max) {
10958                 cell.className = " fc-state-disabled";
10959                 cell.title = cal.maxText;
10960                 return;
10961             }
10962             if(ddays){
10963                 if(ddays.indexOf(d.getDay()) != -1){
10964                     cell.title = ddaysText;
10965                     cell.className = " fc-state-disabled";
10966                 }
10967             }
10968             if(ddMatch && format){
10969                 var fvalue = d.dateFormat(format);
10970                 if(ddMatch.test(fvalue)){
10971                     cell.title = ddText.replace("%0", fvalue);
10972                     cell.className = " fc-state-disabled";
10973                 }
10974             }
10975             
10976             if (!cell.initialClassName) {
10977                 cell.initialClassName = cell.dom.className;
10978             }
10979             
10980             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10981         };
10982
10983         var i = 0;
10984         
10985         for(; i < startingPos; i++) {
10986             textEls[i].innerHTML = (++prevStart);
10987             d.setDate(d.getDate()+1);
10988             
10989             cells[i].className = "fc-past fc-other-month";
10990             setCellClass(this, cells[i]);
10991         }
10992         
10993         var intDay = 0;
10994         
10995         for(; i < days; i++){
10996             intDay = i - startingPos + 1;
10997             textEls[i].innerHTML = (intDay);
10998             d.setDate(d.getDate()+1);
10999             
11000             cells[i].className = ''; // "x-date-active";
11001             setCellClass(this, cells[i]);
11002         }
11003         var extraDays = 0;
11004         
11005         for(; i < 42; i++) {
11006             textEls[i].innerHTML = (++extraDays);
11007             d.setDate(d.getDate()+1);
11008             
11009             cells[i].className = "fc-future fc-other-month";
11010             setCellClass(this, cells[i]);
11011         }
11012         
11013         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11014         
11015         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11016         
11017         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11018         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11019         
11020         if(totalRows != 6){
11021             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11022             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11023         }
11024         
11025         this.fireEvent('monthchange', this, date);
11026         
11027         
11028         /*
11029         if(!this.internalRender){
11030             var main = this.el.dom.firstChild;
11031             var w = main.offsetWidth;
11032             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11033             Roo.fly(main).setWidth(w);
11034             this.internalRender = true;
11035             // opera does not respect the auto grow header center column
11036             // then, after it gets a width opera refuses to recalculate
11037             // without a second pass
11038             if(Roo.isOpera && !this.secondPass){
11039                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11040                 this.secondPass = true;
11041                 this.update.defer(10, this, [date]);
11042             }
11043         }
11044         */
11045         
11046     },
11047     
11048     findCell : function(dt) {
11049         dt = dt.clearTime().getTime();
11050         var ret = false;
11051         this.cells.each(function(c){
11052             //Roo.log("check " +c.dateValue + '?=' + dt);
11053             if(c.dateValue == dt){
11054                 ret = c;
11055                 return false;
11056             }
11057             return true;
11058         });
11059         
11060         return ret;
11061     },
11062     
11063     findCells : function(ev) {
11064         var s = ev.start.clone().clearTime().getTime();
11065        // Roo.log(s);
11066         var e= ev.end.clone().clearTime().getTime();
11067        // Roo.log(e);
11068         var ret = [];
11069         this.cells.each(function(c){
11070              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11071             
11072             if(c.dateValue > e){
11073                 return ;
11074             }
11075             if(c.dateValue < s){
11076                 return ;
11077             }
11078             ret.push(c);
11079         });
11080         
11081         return ret;    
11082     },
11083     
11084     findBestRow: function(cells)
11085     {
11086         var ret = 0;
11087         
11088         for (var i =0 ; i < cells.length;i++) {
11089             ret  = Math.max(cells[i].rows || 0,ret);
11090         }
11091         return ret;
11092         
11093     },
11094     
11095     
11096     addItem : function(ev)
11097     {
11098         // look for vertical location slot in
11099         var cells = this.findCells(ev);
11100         
11101         ev.row = this.findBestRow(cells);
11102         
11103         // work out the location.
11104         
11105         var crow = false;
11106         var rows = [];
11107         for(var i =0; i < cells.length; i++) {
11108             if (!crow) {
11109                 crow = {
11110                     start : cells[i],
11111                     end :  cells[i]
11112                 };
11113                 continue;
11114             }
11115             if (crow.start.getY() == cells[i].getY()) {
11116                 // on same row.
11117                 crow.end = cells[i];
11118                 continue;
11119             }
11120             // different row.
11121             rows.push(crow);
11122             crow = {
11123                 start: cells[i],
11124                 end : cells[i]
11125             };
11126             
11127         }
11128         
11129         rows.push(crow);
11130         ev.els = [];
11131         ev.rows = rows;
11132         ev.cells = cells;
11133         for (var i = 0; i < cells.length;i++) {
11134             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11135             
11136         }
11137         
11138         this.calevents.push(ev);
11139     },
11140     
11141     clearEvents: function() {
11142         
11143         if(!this.calevents){
11144             return;
11145         }
11146         
11147         Roo.each(this.cells.elements, function(c){
11148             c.rows = 0;
11149         });
11150         
11151         Roo.each(this.calevents, function(e) {
11152             Roo.each(e.els, function(el) {
11153                 el.un('mouseenter' ,this.onEventEnter, this);
11154                 el.un('mouseleave' ,this.onEventLeave, this);
11155                 el.remove();
11156             },this);
11157         },this);
11158         
11159     },
11160     
11161     renderEvents: function()
11162     {   
11163         // first make sure there is enough space..
11164         
11165         this.cells.each(function(c) {
11166         
11167             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11168         });
11169         
11170         for (var e = 0; e < this.calevents.length; e++) {
11171             var ev = this.calevents[e];
11172             var cells = ev.cells;
11173             var rows = ev.rows;
11174             
11175             for(var i =0; i < rows.length; i++) {
11176                 
11177                  
11178                 // how many rows should it span..
11179                 
11180                 var  cfg = {
11181                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11182                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11183                     
11184                     unselectable : "on",
11185                     cn : [
11186                         {
11187                             cls: 'fc-event-inner',
11188                             cn : [
11189 //                                {
11190 //                                  tag:'span',
11191 //                                  cls: 'fc-event-time',
11192 //                                  html : cells.length > 1 ? '' : ev.time
11193 //                                },
11194                                 {
11195                                   tag:'span',
11196                                   cls: 'fc-event-title',
11197                                   html : String.format('{0}', ev.title)
11198                                 }
11199                                 
11200                                 
11201                             ]
11202                         },
11203                         {
11204                             cls: 'ui-resizable-handle ui-resizable-e',
11205                             html : '&nbsp;&nbsp;&nbsp'
11206                         }
11207                         
11208                     ]
11209                 };
11210                 if (i == 0) {
11211                     cfg.cls += ' fc-event-start';
11212                 }
11213                 if ((i+1) == rows.length) {
11214                     cfg.cls += ' fc-event-end';
11215                 }
11216                 
11217                 var ctr = this.el.select('.fc-event-container',true).first();
11218                 var cg = ctr.createChild(cfg);
11219                 
11220                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11221                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11222                 cg.on('click', this.onEventClick, this, ev);
11223                 
11224                 ev.els.push(cg);
11225                 
11226                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11227                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11228                 //Roo.log(cg);
11229                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11230                 cg.setWidth(ebox.right - sbox.x -2);
11231             }
11232             
11233             
11234         }
11235         
11236     },
11237     
11238     onEventEnter: function (e, el,event,d) {
11239         this.fireEvent('evententer', this, el, event);
11240     },
11241     
11242     onEventLeave: function (e, el,event,d) {
11243         this.fireEvent('eventleave', this, el, event);
11244     },
11245     
11246     onEventClick: function (e, el,event,d) {
11247         this.fireEvent('eventclick', this, el, event);
11248     },
11249     
11250     onMonthChange: function () {
11251         this.store.load();
11252     },
11253     
11254     onLoad: function () 
11255     {   
11256         this.calevents = [];
11257         var cal = this;
11258         
11259         if(this.store.getCount() > 0){
11260             this.store.data.each(function(d){
11261                cal.addItem({
11262                     id : d.data.id,
11263                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11264                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11265                     time : d.data.start_time,
11266                     title : d.data.title,
11267                     description : d.data.description,
11268                     venue : d.data.venue
11269                 });
11270             });
11271         }
11272         
11273         this.renderEvents();
11274         
11275         if(this.loadMask){
11276             this.maskEl.hide();
11277         }
11278     },
11279     
11280     onBeforeLoad: function()
11281     {
11282         this.clearEvents();
11283         
11284         if(this.loadMask){
11285             this.maskEl.show();
11286         }
11287     }
11288 });
11289
11290  
11291  /*
11292  * - LGPL
11293  *
11294  * element
11295  * 
11296  */
11297
11298 /**
11299  * @class Roo.bootstrap.Popover
11300  * @extends Roo.bootstrap.Component
11301  * Bootstrap Popover class
11302  * @cfg {String} html contents of the popover   (or false to use children..)
11303  * @cfg {String} title of popover (or false to hide)
11304  * @cfg {String} placement how it is placed
11305  * @cfg {String} trigger click || hover (or false to trigger manually)
11306  * @cfg {String} over what (parent or false to trigger manually.)
11307  * 
11308  * @constructor
11309  * Create a new Popover
11310  * @param {Object} config The config object
11311  */
11312
11313 Roo.bootstrap.Popover = function(config){
11314     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11315 };
11316
11317 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11318     
11319     title: 'Fill in a title',
11320     html: false,
11321     
11322     placement : 'right',
11323     trigger : 'hover', // hover
11324     
11325     over: 'parent',
11326     
11327     can_build_overlaid : false,
11328     
11329     getChildContainer : function()
11330     {
11331         return this.el.select('.popover-content',true).first();
11332     },
11333     
11334     getAutoCreate : function(){
11335          Roo.log('make popover?');
11336         var cfg = {
11337            cls : 'popover roo-dynamic',
11338            style: 'display:block',
11339            cn : [
11340                 {
11341                     cls : 'arrow'
11342                 },
11343                 {
11344                     cls : 'popover-inner',
11345                     cn : [
11346                         {
11347                             tag: 'h3',
11348                             cls: 'popover-title',
11349                             html : this.title
11350                         },
11351                         {
11352                             cls : 'popover-content',
11353                             html : this.html
11354                         }
11355                     ]
11356                     
11357                 }
11358            ]
11359         };
11360         
11361         return cfg;
11362     },
11363     setTitle: function(str)
11364     {
11365         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11366     },
11367     setContent: function(str)
11368     {
11369         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11370     },
11371     // as it get's added to the bottom of the page.
11372     onRender : function(ct, position)
11373     {
11374         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11375         if(!this.el){
11376             var cfg = Roo.apply({},  this.getAutoCreate());
11377             cfg.id = Roo.id();
11378             
11379             if (this.cls) {
11380                 cfg.cls += ' ' + this.cls;
11381             }
11382             if (this.style) {
11383                 cfg.style = this.style;
11384             }
11385             Roo.log("adding to ")
11386             this.el = Roo.get(document.body).createChild(cfg, position);
11387             Roo.log(this.el);
11388         }
11389         this.initEvents();
11390     },
11391     
11392     initEvents : function()
11393     {
11394         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11395         this.el.enableDisplayMode('block');
11396         this.el.hide();
11397         if (this.over === false) {
11398             return; 
11399         }
11400         if (this.triggers === false) {
11401             return;
11402         }
11403         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11404         var triggers = this.trigger ? this.trigger.split(' ') : [];
11405         Roo.each(triggers, function(trigger) {
11406         
11407             if (trigger == 'click') {
11408                 on_el.on('click', this.toggle, this);
11409             } else if (trigger != 'manual') {
11410                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11411                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11412       
11413                 on_el.on(eventIn  ,this.enter, this);
11414                 on_el.on(eventOut, this.leave, this);
11415             }
11416         }, this);
11417         
11418     },
11419     
11420     
11421     // private
11422     timeout : null,
11423     hoverState : null,
11424     
11425     toggle : function () {
11426         this.hoverState == 'in' ? this.leave() : this.enter();
11427     },
11428     
11429     enter : function () {
11430        
11431     
11432         clearTimeout(this.timeout);
11433     
11434         this.hoverState = 'in'
11435     
11436         if (!this.delay || !this.delay.show) {
11437             this.show();
11438             return 
11439         }
11440         var _t = this;
11441         this.timeout = setTimeout(function () {
11442             if (_t.hoverState == 'in') {
11443                 _t.show();
11444             }
11445         }, this.delay.show)
11446     },
11447     leave : function() {
11448         clearTimeout(this.timeout);
11449     
11450         this.hoverState = 'out'
11451     
11452         if (!this.delay || !this.delay.hide) {
11453             this.hide();
11454             return 
11455         }
11456         var _t = this;
11457         this.timeout = setTimeout(function () {
11458             if (_t.hoverState == 'out') {
11459                 _t.hide();
11460             }
11461         }, this.delay.hide)
11462     },
11463     
11464     show : function (on_el)
11465     {
11466         if (!on_el) {
11467             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11468         }
11469         // set content.
11470         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11471         if (this.html !== false) {
11472             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11473         }
11474         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11475         if (!this.title.length) {
11476             this.el.select('.popover-title',true).hide();
11477         }
11478         
11479         var placement = typeof this.placement == 'function' ?
11480             this.placement.call(this, this.el, on_el) :
11481             this.placement;
11482             
11483         var autoToken = /\s?auto?\s?/i;
11484         var autoPlace = autoToken.test(placement);
11485         if (autoPlace) {
11486             placement = placement.replace(autoToken, '') || 'top';
11487         }
11488         
11489         //this.el.detach()
11490         //this.el.setXY([0,0]);
11491         this.el.show();
11492         this.el.dom.style.display='block';
11493         this.el.addClass(placement);
11494         
11495         //this.el.appendTo(on_el);
11496         
11497         var p = this.getPosition();
11498         var box = this.el.getBox();
11499         
11500         if (autoPlace) {
11501             // fixme..
11502         }
11503         var align = Roo.bootstrap.Popover.alignment[placement]
11504         this.el.alignTo(on_el, align[0],align[1]);
11505         //var arrow = this.el.select('.arrow',true).first();
11506         //arrow.set(align[2], 
11507         
11508         this.el.addClass('in');
11509         this.hoverState = null;
11510         
11511         if (this.el.hasClass('fade')) {
11512             // fade it?
11513         }
11514         
11515     },
11516     hide : function()
11517     {
11518         this.el.setXY([0,0]);
11519         this.el.removeClass('in');
11520         this.el.hide();
11521         
11522     }
11523     
11524 });
11525
11526 Roo.bootstrap.Popover.alignment = {
11527     'left' : ['r-l', [-10,0], 'right'],
11528     'right' : ['l-r', [10,0], 'left'],
11529     'bottom' : ['t-b', [0,10], 'top'],
11530     'top' : [ 'b-t', [0,-10], 'bottom']
11531 };
11532
11533  /*
11534  * - LGPL
11535  *
11536  * Progress
11537  * 
11538  */
11539
11540 /**
11541  * @class Roo.bootstrap.Progress
11542  * @extends Roo.bootstrap.Component
11543  * Bootstrap Progress class
11544  * @cfg {Boolean} striped striped of the progress bar
11545  * @cfg {Boolean} active animated of the progress bar
11546  * 
11547  * 
11548  * @constructor
11549  * Create a new Progress
11550  * @param {Object} config The config object
11551  */
11552
11553 Roo.bootstrap.Progress = function(config){
11554     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11555 };
11556
11557 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
11558     
11559     striped : false,
11560     active: false,
11561     
11562     getAutoCreate : function(){
11563         var cfg = {
11564             tag: 'div',
11565             cls: 'progress'
11566         };
11567         
11568         
11569         if(this.striped){
11570             cfg.cls += ' progress-striped';
11571         }
11572       
11573         if(this.active){
11574             cfg.cls += ' active';
11575         }
11576         
11577         
11578         return cfg;
11579     }
11580    
11581 });
11582
11583  
11584
11585  /*
11586  * - LGPL
11587  *
11588  * ProgressBar
11589  * 
11590  */
11591
11592 /**
11593  * @class Roo.bootstrap.ProgressBar
11594  * @extends Roo.bootstrap.Component
11595  * Bootstrap ProgressBar class
11596  * @cfg {Number} aria_valuenow aria-value now
11597  * @cfg {Number} aria_valuemin aria-value min
11598  * @cfg {Number} aria_valuemax aria-value max
11599  * @cfg {String} label label for the progress bar
11600  * @cfg {String} panel (success | info | warning | danger )
11601  * @cfg {String} role role of the progress bar
11602  * @cfg {String} sr_only text
11603  * 
11604  * 
11605  * @constructor
11606  * Create a new ProgressBar
11607  * @param {Object} config The config object
11608  */
11609
11610 Roo.bootstrap.ProgressBar = function(config){
11611     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11612 };
11613
11614 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
11615     
11616     aria_valuenow : 0,
11617     aria_valuemin : 0,
11618     aria_valuemax : 100,
11619     label : false,
11620     panel : false,
11621     role : false,
11622     sr_only: false,
11623     
11624     getAutoCreate : function()
11625     {
11626         
11627         var cfg = {
11628             tag: 'div',
11629             cls: 'progress-bar',
11630             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11631         };
11632         
11633         if(this.sr_only){
11634             cfg.cn = {
11635                 tag: 'span',
11636                 cls: 'sr-only',
11637                 html: this.sr_only
11638             }
11639         }
11640         
11641         if(this.role){
11642             cfg.role = this.role;
11643         }
11644         
11645         if(this.aria_valuenow){
11646             cfg['aria-valuenow'] = this.aria_valuenow;
11647         }
11648         
11649         if(this.aria_valuemin){
11650             cfg['aria-valuemin'] = this.aria_valuemin;
11651         }
11652         
11653         if(this.aria_valuemax){
11654             cfg['aria-valuemax'] = this.aria_valuemax;
11655         }
11656         
11657         if(this.label && !this.sr_only){
11658             cfg.html = this.label;
11659         }
11660         
11661         if(this.panel){
11662             cfg.cls += ' progress-bar-' + this.panel;
11663         }
11664         
11665         return cfg;
11666     },
11667     
11668     update : function(aria_valuenow)
11669     {
11670         this.aria_valuenow = aria_valuenow;
11671         
11672         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11673     }
11674    
11675 });
11676
11677  
11678
11679  /*
11680  * - LGPL
11681  *
11682  * TabPanel
11683  * 
11684  */
11685
11686 /**
11687  * @class Roo.bootstrap.TabPanel
11688  * @extends Roo.bootstrap.Component
11689  * Bootstrap TabPanel class
11690  * @cfg {Boolean} active panel active
11691  * @cfg {String} html panel content
11692  * @cfg {String} tabId tab relate id
11693  * @cfg {String} navId The navbar which triggers show hide
11694  * 
11695  * 
11696  * @constructor
11697  * Create a new TabPanel
11698  * @param {Object} config The config object
11699  */
11700
11701 Roo.bootstrap.TabPanel = function(config){
11702     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11703      this.addEvents({
11704         /**
11705              * @event changed
11706              * Fires when the active status changes
11707              * @param {Roo.bootstrap.TabPanel} this
11708              * @param {Boolean} state the new state
11709             
11710          */
11711         'changed': true
11712      });
11713 };
11714
11715 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
11716     
11717     active: false,
11718     html: false,
11719     tabId: false,
11720     navId : false,
11721     
11722     getAutoCreate : function(){
11723         var cfg = {
11724             tag: 'div',
11725             cls: 'tab-pane',
11726             html: this.html || ''
11727         };
11728         
11729         if(this.active){
11730             cfg.cls += ' active';
11731         }
11732         
11733         if(this.tabId){
11734             cfg.tabId = this.tabId;
11735         }
11736         
11737         return cfg;
11738     },
11739     onRender : function(ct, position)
11740     {
11741        // Roo.log("Call onRender: " + this.xtype);
11742         
11743         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11744         
11745         if (this.navId && this.tabId) {
11746             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11747             if (!item) {
11748                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
11749             } else {
11750                 item.on('changed', function(item, state) {
11751                     this.setActive(state);
11752                 }, this);
11753             }
11754         }
11755         
11756     },
11757     setActive: function(state)
11758     {
11759         Roo.log("panel - set active " + this.tabId + "=" + state);
11760         
11761         this.active = state;
11762         if (!state) {
11763             this.el.removeClass('active');
11764             
11765         } else  if (!this.el.hasClass('active')) {
11766             this.el.addClass('active');
11767         }
11768         this.fireEvent('changed', this, state);
11769     }
11770     
11771     
11772 });
11773  
11774
11775  
11776
11777  /*
11778  * - LGPL
11779  *
11780  * DateField
11781  * 
11782  */
11783
11784 /**
11785  * @class Roo.bootstrap.DateField
11786  * @extends Roo.bootstrap.Input
11787  * Bootstrap DateField class
11788  * @cfg {Number} weekStart default 0
11789  * @cfg {Number} weekStart default 0
11790  * @cfg {Number} viewMode default empty, (months|years)
11791  * @cfg {Number} minViewMode default empty, (months|years)
11792  * @cfg {Number} startDate default -Infinity
11793  * @cfg {Number} endDate default Infinity
11794  * @cfg {Boolean} todayHighlight default false
11795  * @cfg {Boolean} todayBtn default false
11796  * @cfg {Boolean} calendarWeeks default false
11797  * @cfg {Object} daysOfWeekDisabled default empty
11798  * 
11799  * @cfg {Boolean} keyboardNavigation default true
11800  * @cfg {String} language default en
11801  * 
11802  * @constructor
11803  * Create a new DateField
11804  * @param {Object} config The config object
11805  */
11806
11807 Roo.bootstrap.DateField = function(config){
11808     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11809      this.addEvents({
11810             /**
11811              * @event show
11812              * Fires when this field show.
11813              * @param {Roo.bootstrap.DateField} this
11814              * @param {Mixed} date The date value
11815              */
11816             show : true,
11817             /**
11818              * @event show
11819              * Fires when this field hide.
11820              * @param {Roo.bootstrap.DateField} this
11821              * @param {Mixed} date The date value
11822              */
11823             hide : true,
11824             /**
11825              * @event select
11826              * Fires when select a date.
11827              * @param {Roo.bootstrap.DateField} this
11828              * @param {Mixed} date The date value
11829              */
11830             select : true
11831         });
11832 };
11833
11834 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11835     
11836     /**
11837      * @cfg {String} format
11838      * The default date format string which can be overriden for localization support.  The format must be
11839      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11840      */
11841     format : "m/d/y",
11842     /**
11843      * @cfg {String} altFormats
11844      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11845      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11846      */
11847     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11848     
11849     weekStart : 0,
11850     
11851     viewMode : '',
11852     
11853     minViewMode : '',
11854     
11855     todayHighlight : false,
11856     
11857     todayBtn: false,
11858     
11859     language: 'en',
11860     
11861     keyboardNavigation: true,
11862     
11863     calendarWeeks: false,
11864     
11865     startDate: -Infinity,
11866     
11867     endDate: Infinity,
11868     
11869     daysOfWeekDisabled: [],
11870     
11871     _events: [],
11872     
11873     UTCDate: function()
11874     {
11875         return new Date(Date.UTC.apply(Date, arguments));
11876     },
11877     
11878     UTCToday: function()
11879     {
11880         var today = new Date();
11881         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11882     },
11883     
11884     getDate: function() {
11885             var d = this.getUTCDate();
11886             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11887     },
11888     
11889     getUTCDate: function() {
11890             return this.date;
11891     },
11892     
11893     setDate: function(d) {
11894             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11895     },
11896     
11897     setUTCDate: function(d) {
11898             this.date = d;
11899             this.setValue(this.formatDate(this.date));
11900     },
11901         
11902     onRender: function(ct, position)
11903     {
11904         
11905         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11906         
11907         this.language = this.language || 'en';
11908         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11909         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11910         
11911         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11912         this.format = this.format || 'm/d/y';
11913         this.isInline = false;
11914         this.isInput = true;
11915         this.component = this.el.select('.add-on', true).first() || false;
11916         this.component = (this.component && this.component.length === 0) ? false : this.component;
11917         this.hasInput = this.component && this.inputEL().length;
11918         
11919         if (typeof(this.minViewMode === 'string')) {
11920             switch (this.minViewMode) {
11921                 case 'months':
11922                     this.minViewMode = 1;
11923                     break;
11924                 case 'years':
11925                     this.minViewMode = 2;
11926                     break;
11927                 default:
11928                     this.minViewMode = 0;
11929                     break;
11930             }
11931         }
11932         
11933         if (typeof(this.viewMode === 'string')) {
11934             switch (this.viewMode) {
11935                 case 'months':
11936                     this.viewMode = 1;
11937                     break;
11938                 case 'years':
11939                     this.viewMode = 2;
11940                     break;
11941                 default:
11942                     this.viewMode = 0;
11943                     break;
11944             }
11945         }
11946                 
11947         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11948         
11949         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11950         
11951         this.picker().on('mousedown', this.onMousedown, this);
11952         this.picker().on('click', this.onClick, this);
11953         
11954         this.picker().addClass('datepicker-dropdown');
11955         
11956         this.startViewMode = this.viewMode;
11957         
11958         
11959         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11960             if(!this.calendarWeeks){
11961                 v.remove();
11962                 return;
11963             };
11964             
11965             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11966             v.attr('colspan', function(i, val){
11967                 return parseInt(val) + 1;
11968             });
11969         })
11970                         
11971         
11972         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11973         
11974         this.setStartDate(this.startDate);
11975         this.setEndDate(this.endDate);
11976         
11977         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11978         
11979         this.fillDow();
11980         this.fillMonths();
11981         this.update();
11982         this.showMode();
11983         
11984         if(this.isInline) {
11985             this.show();
11986         }
11987     },
11988     
11989     picker : function()
11990     {
11991         return this.el.select('.datepicker', true).first();
11992     },
11993     
11994     fillDow: function()
11995     {
11996         var dowCnt = this.weekStart;
11997         
11998         var dow = {
11999             tag: 'tr',
12000             cn: [
12001                 
12002             ]
12003         };
12004         
12005         if(this.calendarWeeks){
12006             dow.cn.push({
12007                 tag: 'th',
12008                 cls: 'cw',
12009                 html: '&nbsp;'
12010             })
12011         }
12012         
12013         while (dowCnt < this.weekStart + 7) {
12014             dow.cn.push({
12015                 tag: 'th',
12016                 cls: 'dow',
12017                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12018             });
12019         }
12020         
12021         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12022     },
12023     
12024     fillMonths: function()
12025     {    
12026         var i = 0
12027         var months = this.picker().select('>.datepicker-months td', true).first();
12028         
12029         months.dom.innerHTML = '';
12030         
12031         while (i < 12) {
12032             var month = {
12033                 tag: 'span',
12034                 cls: 'month',
12035                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12036             }
12037             
12038             months.createChild(month);
12039         }
12040         
12041     },
12042     
12043     update: function(){
12044         
12045         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12046         
12047         if (this.date < this.startDate) {
12048             this.viewDate = new Date(this.startDate);
12049         } else if (this.date > this.endDate) {
12050             this.viewDate = new Date(this.endDate);
12051         } else {
12052             this.viewDate = new Date(this.date);
12053         }
12054         
12055         this.fill();
12056     },
12057     
12058     fill: function() {
12059         var d = new Date(this.viewDate),
12060                 year = d.getUTCFullYear(),
12061                 month = d.getUTCMonth(),
12062                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12063                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12064                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12065                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12066                 currentDate = this.date && this.date.valueOf(),
12067                 today = this.UTCToday();
12068         
12069         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12070         
12071 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12072         
12073 //        this.picker.select('>tfoot th.today').
12074 //                                              .text(dates[this.language].today)
12075 //                                              .toggle(this.todayBtn !== false);
12076     
12077         this.updateNavArrows();
12078         this.fillMonths();
12079                                                 
12080         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12081         
12082         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12083          
12084         prevMonth.setUTCDate(day);
12085         
12086         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12087         
12088         var nextMonth = new Date(prevMonth);
12089         
12090         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12091         
12092         nextMonth = nextMonth.valueOf();
12093         
12094         var fillMonths = false;
12095         
12096         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12097         
12098         while(prevMonth.valueOf() < nextMonth) {
12099             var clsName = '';
12100             
12101             if (prevMonth.getUTCDay() === this.weekStart) {
12102                 if(fillMonths){
12103                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12104                 }
12105                     
12106                 fillMonths = {
12107                     tag: 'tr',
12108                     cn: []
12109                 };
12110                 
12111                 if(this.calendarWeeks){
12112                     // ISO 8601: First week contains first thursday.
12113                     // ISO also states week starts on Monday, but we can be more abstract here.
12114                     var
12115                     // Start of current week: based on weekstart/current date
12116                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12117                     // Thursday of this week
12118                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12119                     // First Thursday of year, year from thursday
12120                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12121                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12122                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12123                     
12124                     fillMonths.cn.push({
12125                         tag: 'td',
12126                         cls: 'cw',
12127                         html: calWeek
12128                     });
12129                 }
12130             }
12131             
12132             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12133                 clsName += ' old';
12134             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12135                 clsName += ' new';
12136             }
12137             if (this.todayHighlight &&
12138                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12139                 prevMonth.getUTCMonth() == today.getMonth() &&
12140                 prevMonth.getUTCDate() == today.getDate()) {
12141                 clsName += ' today';
12142             }
12143             
12144             if (currentDate && prevMonth.valueOf() === currentDate) {
12145                 clsName += ' active';
12146             }
12147             
12148             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12149                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12150                     clsName += ' disabled';
12151             }
12152             
12153             fillMonths.cn.push({
12154                 tag: 'td',
12155                 cls: 'day ' + clsName,
12156                 html: prevMonth.getDate()
12157             })
12158             
12159             prevMonth.setDate(prevMonth.getDate()+1);
12160         }
12161           
12162         var currentYear = this.date && this.date.getUTCFullYear();
12163         var currentMonth = this.date && this.date.getUTCMonth();
12164         
12165         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12166         
12167         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12168             v.removeClass('active');
12169             
12170             if(currentYear === year && k === currentMonth){
12171                 v.addClass('active');
12172             }
12173             
12174             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12175                 v.addClass('disabled');
12176             }
12177             
12178         });
12179         
12180         
12181         year = parseInt(year/10, 10) * 10;
12182         
12183         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12184         
12185         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12186         
12187         year -= 1;
12188         for (var i = -1; i < 11; i++) {
12189             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12190                 tag: 'span',
12191                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12192                 html: year
12193             })
12194             
12195             year += 1;
12196         }
12197     },
12198     
12199     showMode: function(dir) {
12200         if (dir) {
12201             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12202         }
12203         Roo.each(this.picker().select('>div',true).elements, function(v){
12204             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12205             v.hide();
12206         });
12207         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12208     },
12209     
12210     place: function()
12211     {
12212         if(this.isInline) return;
12213         
12214         this.picker().removeClass(['bottom', 'top']);
12215         
12216         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12217             /*
12218              * place to the top of element!
12219              *
12220              */
12221             
12222             this.picker().addClass('top');
12223             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12224             
12225             return;
12226         }
12227         
12228         this.picker().addClass('bottom');
12229         
12230         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12231     },
12232     
12233     parseDate : function(value){
12234         if(!value || value instanceof Date){
12235             return value;
12236         }
12237         var v = Date.parseDate(value, this.format);
12238         if (!v && this.useIso) {
12239             v = Date.parseDate(value, 'Y-m-d');
12240         }
12241         if(!v && this.altFormats){
12242             if(!this.altFormatsArray){
12243                 this.altFormatsArray = this.altFormats.split("|");
12244             }
12245             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12246                 v = Date.parseDate(value, this.altFormatsArray[i]);
12247             }
12248         }
12249         return v;
12250     },
12251     
12252     formatDate : function(date, fmt){
12253         return (!date || !(date instanceof Date)) ?
12254         date : date.dateFormat(fmt || this.format);
12255     },
12256     
12257     onFocus : function()
12258     {
12259         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12260         this.show();
12261     },
12262     
12263     onBlur : function()
12264     {
12265         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12266         this.hide();
12267     },
12268     
12269     show : function()
12270     {
12271         this.picker().show();
12272         this.update();
12273         this.place();
12274         
12275         this.fireEvent('show', this, this.date);
12276     },
12277     
12278     hide : function()
12279     {
12280         if(this.isInline) return;
12281         this.picker().hide();
12282         this.viewMode = this.startViewMode;
12283         this.showMode();
12284         
12285         this.fireEvent('hide', this, this.date);
12286         
12287     },
12288     
12289     onMousedown: function(e){
12290         e.stopPropagation();
12291         e.preventDefault();
12292     },
12293     
12294     keyup: function(e){
12295         Roo.bootstrap.DateField.superclass.keyup.call(this);
12296         this.update();
12297         
12298     },
12299
12300     setValue: function(v){
12301         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12302         
12303         this.fireEvent('select', this, this.date);
12304         
12305     },
12306     
12307     fireKey: function(e){
12308         if (!this.picker().isVisible()){
12309             if (e.keyCode == 27) // allow escape to hide and re-show picker
12310                 this.show();
12311             return;
12312         }
12313         var dateChanged = false,
12314         dir, day, month,
12315         newDate, newViewDate;
12316         switch(e.keyCode){
12317             case 27: // escape
12318                 this.hide();
12319                 e.preventDefault();
12320                 break;
12321             case 37: // left
12322             case 39: // right
12323                 if (!this.keyboardNavigation) break;
12324                 dir = e.keyCode == 37 ? -1 : 1;
12325                 
12326                 if (e.ctrlKey){
12327                     newDate = this.moveYear(this.date, dir);
12328                     newViewDate = this.moveYear(this.viewDate, dir);
12329                 } else if (e.shiftKey){
12330                     newDate = this.moveMonth(this.date, dir);
12331                     newViewDate = this.moveMonth(this.viewDate, dir);
12332                 } else {
12333                     newDate = new Date(this.date);
12334                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12335                     newViewDate = new Date(this.viewDate);
12336                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12337                 }
12338                 if (this.dateWithinRange(newDate)){
12339                     this.date = newDate;
12340                     this.viewDate = newViewDate;
12341                     this.setValue(this.formatDate(this.date));
12342                     this.update();
12343                     e.preventDefault();
12344                     dateChanged = true;
12345                 }
12346                 break;
12347             case 38: // up
12348             case 40: // down
12349                 if (!this.keyboardNavigation) break;
12350                 dir = e.keyCode == 38 ? -1 : 1;
12351                 if (e.ctrlKey){
12352                     newDate = this.moveYear(this.date, dir);
12353                     newViewDate = this.moveYear(this.viewDate, dir);
12354                 } else if (e.shiftKey){
12355                     newDate = this.moveMonth(this.date, dir);
12356                     newViewDate = this.moveMonth(this.viewDate, dir);
12357                 } else {
12358                     newDate = new Date(this.date);
12359                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12360                     newViewDate = new Date(this.viewDate);
12361                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12362                 }
12363                 if (this.dateWithinRange(newDate)){
12364                     this.date = newDate;
12365                     this.viewDate = newViewDate;
12366                     this.setValue(this.formatDate(this.date));
12367                     this.update();
12368                     e.preventDefault();
12369                     dateChanged = true;
12370                 }
12371                 break;
12372             case 13: // enter
12373                 this.setValue(this.formatDate(this.date));
12374                 this.hide();
12375                 e.preventDefault();
12376                 break;
12377             case 9: // tab
12378                 this.setValue(this.formatDate(this.date));
12379                 this.hide();
12380                 break;
12381         }
12382     },
12383     
12384     
12385     onClick: function(e) {
12386         e.stopPropagation();
12387         e.preventDefault();
12388         
12389         var target = e.getTarget();
12390         
12391         if(target.nodeName.toLowerCase() === 'i'){
12392             target = Roo.get(target).dom.parentNode;
12393         }
12394         
12395         var nodeName = target.nodeName;
12396         var className = target.className;
12397         var html = target.innerHTML;
12398         
12399         switch(nodeName.toLowerCase()) {
12400             case 'th':
12401                 switch(className) {
12402                     case 'switch':
12403                         this.showMode(1);
12404                         break;
12405                     case 'prev':
12406                     case 'next':
12407                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12408                         switch(this.viewMode){
12409                                 case 0:
12410                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12411                                         break;
12412                                 case 1:
12413                                 case 2:
12414                                         this.viewDate = this.moveYear(this.viewDate, dir);
12415                                         break;
12416                         }
12417                         this.fill();
12418                         break;
12419                     case 'today':
12420                         var date = new Date();
12421                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12422                         this.fill()
12423                         this.setValue(this.formatDate(this.date));
12424                         this.hide();
12425                         break;
12426                 }
12427                 break;
12428             case 'span':
12429                 if (className.indexOf('disabled') === -1) {
12430                     this.viewDate.setUTCDate(1);
12431                     if (className.indexOf('month') !== -1) {
12432                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12433                     } else {
12434                         var year = parseInt(html, 10) || 0;
12435                         this.viewDate.setUTCFullYear(year);
12436                         
12437                     }
12438                     this.showMode(-1);
12439                     this.fill();
12440                 }
12441                 break;
12442                 
12443             case 'td':
12444                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12445                     var day = parseInt(html, 10) || 1;
12446                     var year = this.viewDate.getUTCFullYear(),
12447                         month = this.viewDate.getUTCMonth();
12448
12449                     if (className.indexOf('old') !== -1) {
12450                         if(month === 0 ){
12451                             month = 11;
12452                             year -= 1;
12453                         }else{
12454                             month -= 1;
12455                         }
12456                     } else if (className.indexOf('new') !== -1) {
12457                         if (month == 11) {
12458                             month = 0;
12459                             year += 1;
12460                         } else {
12461                             month += 1;
12462                         }
12463                     }
12464                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12465                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12466                     this.fill();
12467                     this.setValue(this.formatDate(this.date));
12468                     this.hide();
12469                 }
12470                 break;
12471         }
12472     },
12473     
12474     setStartDate: function(startDate){
12475         this.startDate = startDate || -Infinity;
12476         if (this.startDate !== -Infinity) {
12477             this.startDate = this.parseDate(this.startDate);
12478         }
12479         this.update();
12480         this.updateNavArrows();
12481     },
12482
12483     setEndDate: function(endDate){
12484         this.endDate = endDate || Infinity;
12485         if (this.endDate !== Infinity) {
12486             this.endDate = this.parseDate(this.endDate);
12487         }
12488         this.update();
12489         this.updateNavArrows();
12490     },
12491     
12492     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12493         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12494         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12495             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12496         }
12497         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12498             return parseInt(d, 10);
12499         });
12500         this.update();
12501         this.updateNavArrows();
12502     },
12503     
12504     updateNavArrows: function() {
12505         var d = new Date(this.viewDate),
12506         year = d.getUTCFullYear(),
12507         month = d.getUTCMonth();
12508         
12509         Roo.each(this.picker().select('.prev', true).elements, function(v){
12510             v.show();
12511             switch (this.viewMode) {
12512                 case 0:
12513
12514                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12515                         v.hide();
12516                     }
12517                     break;
12518                 case 1:
12519                 case 2:
12520                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12521                         v.hide();
12522                     }
12523                     break;
12524             }
12525         });
12526         
12527         Roo.each(this.picker().select('.next', true).elements, function(v){
12528             v.show();
12529             switch (this.viewMode) {
12530                 case 0:
12531
12532                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12533                         v.hide();
12534                     }
12535                     break;
12536                 case 1:
12537                 case 2:
12538                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12539                         v.hide();
12540                     }
12541                     break;
12542             }
12543         })
12544     },
12545     
12546     moveMonth: function(date, dir){
12547         if (!dir) return date;
12548         var new_date = new Date(date.valueOf()),
12549         day = new_date.getUTCDate(),
12550         month = new_date.getUTCMonth(),
12551         mag = Math.abs(dir),
12552         new_month, test;
12553         dir = dir > 0 ? 1 : -1;
12554         if (mag == 1){
12555             test = dir == -1
12556             // If going back one month, make sure month is not current month
12557             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12558             ? function(){
12559                 return new_date.getUTCMonth() == month;
12560             }
12561             // If going forward one month, make sure month is as expected
12562             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12563             : function(){
12564                 return new_date.getUTCMonth() != new_month;
12565             };
12566             new_month = month + dir;
12567             new_date.setUTCMonth(new_month);
12568             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12569             if (new_month < 0 || new_month > 11)
12570                 new_month = (new_month + 12) % 12;
12571         } else {
12572             // For magnitudes >1, move one month at a time...
12573             for (var i=0; i<mag; i++)
12574                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12575                 new_date = this.moveMonth(new_date, dir);
12576             // ...then reset the day, keeping it in the new month
12577             new_month = new_date.getUTCMonth();
12578             new_date.setUTCDate(day);
12579             test = function(){
12580                 return new_month != new_date.getUTCMonth();
12581             };
12582         }
12583         // Common date-resetting loop -- if date is beyond end of month, make it
12584         // end of month
12585         while (test()){
12586             new_date.setUTCDate(--day);
12587             new_date.setUTCMonth(new_month);
12588         }
12589         return new_date;
12590     },
12591
12592     moveYear: function(date, dir){
12593         return this.moveMonth(date, dir*12);
12594     },
12595
12596     dateWithinRange: function(date){
12597         return date >= this.startDate && date <= this.endDate;
12598     },
12599
12600     
12601     remove: function() {
12602         this.picker().remove();
12603     }
12604    
12605 });
12606
12607 Roo.apply(Roo.bootstrap.DateField,  {
12608     
12609     head : {
12610         tag: 'thead',
12611         cn: [
12612         {
12613             tag: 'tr',
12614             cn: [
12615             {
12616                 tag: 'th',
12617                 cls: 'prev',
12618                 html: '<i class="icon-arrow-left"/>'
12619             },
12620             {
12621                 tag: 'th',
12622                 cls: 'switch',
12623                 colspan: '5'
12624             },
12625             {
12626                 tag: 'th',
12627                 cls: 'next',
12628                 html: '<i class="icon-arrow-right"/>'
12629             }
12630
12631             ]
12632         }
12633         ]
12634     },
12635     
12636     content : {
12637         tag: 'tbody',
12638         cn: [
12639         {
12640             tag: 'tr',
12641             cn: [
12642             {
12643                 tag: 'td',
12644                 colspan: '7'
12645             }
12646             ]
12647         }
12648         ]
12649     },
12650     
12651     footer : {
12652         tag: 'tfoot',
12653         cn: [
12654         {
12655             tag: 'tr',
12656             cn: [
12657             {
12658                 tag: 'th',
12659                 colspan: '7',
12660                 cls: 'today'
12661             }
12662                     
12663             ]
12664         }
12665         ]
12666     },
12667     
12668     dates:{
12669         en: {
12670             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12671             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12672             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12673             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12674             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12675             today: "Today"
12676         }
12677     },
12678     
12679     modes: [
12680     {
12681         clsName: 'days',
12682         navFnc: 'Month',
12683         navStep: 1
12684     },
12685     {
12686         clsName: 'months',
12687         navFnc: 'FullYear',
12688         navStep: 1
12689     },
12690     {
12691         clsName: 'years',
12692         navFnc: 'FullYear',
12693         navStep: 10
12694     }]
12695 });
12696
12697 Roo.apply(Roo.bootstrap.DateField,  {
12698   
12699     template : {
12700         tag: 'div',
12701         cls: 'datepicker dropdown-menu',
12702         cn: [
12703         {
12704             tag: 'div',
12705             cls: 'datepicker-days',
12706             cn: [
12707             {
12708                 tag: 'table',
12709                 cls: 'table-condensed',
12710                 cn:[
12711                 Roo.bootstrap.DateField.head,
12712                 {
12713                     tag: 'tbody'
12714                 },
12715                 Roo.bootstrap.DateField.footer
12716                 ]
12717             }
12718             ]
12719         },
12720         {
12721             tag: 'div',
12722             cls: 'datepicker-months',
12723             cn: [
12724             {
12725                 tag: 'table',
12726                 cls: 'table-condensed',
12727                 cn:[
12728                 Roo.bootstrap.DateField.head,
12729                 Roo.bootstrap.DateField.content,
12730                 Roo.bootstrap.DateField.footer
12731                 ]
12732             }
12733             ]
12734         },
12735         {
12736             tag: 'div',
12737             cls: 'datepicker-years',
12738             cn: [
12739             {
12740                 tag: 'table',
12741                 cls: 'table-condensed',
12742                 cn:[
12743                 Roo.bootstrap.DateField.head,
12744                 Roo.bootstrap.DateField.content,
12745                 Roo.bootstrap.DateField.footer
12746                 ]
12747             }
12748             ]
12749         }
12750         ]
12751     }
12752 });
12753
12754  
12755
12756  /*
12757  * - LGPL
12758  *
12759  * TimeField
12760  * 
12761  */
12762
12763 /**
12764  * @class Roo.bootstrap.TimeField
12765  * @extends Roo.bootstrap.Input
12766  * Bootstrap DateField class
12767  * 
12768  * 
12769  * @constructor
12770  * Create a new TimeField
12771  * @param {Object} config The config object
12772  */
12773
12774 Roo.bootstrap.TimeField = function(config){
12775     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12776     this.addEvents({
12777             /**
12778              * @event show
12779              * Fires when this field show.
12780              * @param {Roo.bootstrap.DateField} this
12781              * @param {Mixed} date The date value
12782              */
12783             show : true,
12784             /**
12785              * @event show
12786              * Fires when this field hide.
12787              * @param {Roo.bootstrap.DateField} this
12788              * @param {Mixed} date The date value
12789              */
12790             hide : true,
12791             /**
12792              * @event select
12793              * Fires when select a date.
12794              * @param {Roo.bootstrap.DateField} this
12795              * @param {Mixed} date The date value
12796              */
12797             select : true
12798         });
12799 };
12800
12801 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12802     
12803     /**
12804      * @cfg {String} format
12805      * The default time format string which can be overriden for localization support.  The format must be
12806      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12807      */
12808     format : "H:i",
12809        
12810     onRender: function(ct, position)
12811     {
12812         
12813         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12814                 
12815         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12816         
12817         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12818         
12819         this.pop = this.picker().select('>.datepicker-time',true).first();
12820         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12821         
12822         this.picker().on('mousedown', this.onMousedown, this);
12823         this.picker().on('click', this.onClick, this);
12824         
12825         this.picker().addClass('datepicker-dropdown');
12826     
12827         this.fillTime();
12828         this.update();
12829             
12830         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12831         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12832         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12833         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12834         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12835         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12836
12837     },
12838     
12839     fireKey: function(e){
12840         if (!this.picker().isVisible()){
12841             if (e.keyCode == 27) // allow escape to hide and re-show picker
12842                 this.show();
12843             return;
12844         }
12845
12846         e.preventDefault();
12847         
12848         switch(e.keyCode){
12849             case 27: // escape
12850                 this.hide();
12851                 break;
12852             case 37: // left
12853             case 39: // right
12854                 this.onTogglePeriod();
12855                 break;
12856             case 38: // up
12857                 this.onIncrementMinutes();
12858                 break;
12859             case 40: // down
12860                 this.onDecrementMinutes();
12861                 break;
12862             case 13: // enter
12863             case 9: // tab
12864                 this.setTime();
12865                 break;
12866         }
12867     },
12868     
12869     onClick: function(e) {
12870         e.stopPropagation();
12871         e.preventDefault();
12872     },
12873     
12874     picker : function()
12875     {
12876         return this.el.select('.datepicker', true).first();
12877     },
12878     
12879     fillTime: function()
12880     {    
12881         var time = this.pop.select('tbody', true).first();
12882         
12883         time.dom.innerHTML = '';
12884         
12885         time.createChild({
12886             tag: 'tr',
12887             cn: [
12888                 {
12889                     tag: 'td',
12890                     cn: [
12891                         {
12892                             tag: 'a',
12893                             href: '#',
12894                             cls: 'btn',
12895                             cn: [
12896                                 {
12897                                     tag: 'span',
12898                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12899                                 }
12900                             ]
12901                         } 
12902                     ]
12903                 },
12904                 {
12905                     tag: 'td',
12906                     cls: 'separator'
12907                 },
12908                 {
12909                     tag: 'td',
12910                     cn: [
12911                         {
12912                             tag: 'a',
12913                             href: '#',
12914                             cls: 'btn',
12915                             cn: [
12916                                 {
12917                                     tag: 'span',
12918                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12919                                 }
12920                             ]
12921                         }
12922                     ]
12923                 },
12924                 {
12925                     tag: 'td',
12926                     cls: 'separator'
12927                 }
12928             ]
12929         });
12930         
12931         time.createChild({
12932             tag: 'tr',
12933             cn: [
12934                 {
12935                     tag: 'td',
12936                     cn: [
12937                         {
12938                             tag: 'span',
12939                             cls: 'timepicker-hour',
12940                             html: '00'
12941                         }  
12942                     ]
12943                 },
12944                 {
12945                     tag: 'td',
12946                     cls: 'separator',
12947                     html: ':'
12948                 },
12949                 {
12950                     tag: 'td',
12951                     cn: [
12952                         {
12953                             tag: 'span',
12954                             cls: 'timepicker-minute',
12955                             html: '00'
12956                         }  
12957                     ]
12958                 },
12959                 {
12960                     tag: 'td',
12961                     cls: 'separator'
12962                 },
12963                 {
12964                     tag: 'td',
12965                     cn: [
12966                         {
12967                             tag: 'button',
12968                             type: 'button',
12969                             cls: 'btn btn-primary period',
12970                             html: 'AM'
12971                             
12972                         }
12973                     ]
12974                 }
12975             ]
12976         });
12977         
12978         time.createChild({
12979             tag: 'tr',
12980             cn: [
12981                 {
12982                     tag: 'td',
12983                     cn: [
12984                         {
12985                             tag: 'a',
12986                             href: '#',
12987                             cls: 'btn',
12988                             cn: [
12989                                 {
12990                                     tag: 'span',
12991                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12992                                 }
12993                             ]
12994                         }
12995                     ]
12996                 },
12997                 {
12998                     tag: 'td',
12999                     cls: 'separator'
13000                 },
13001                 {
13002                     tag: 'td',
13003                     cn: [
13004                         {
13005                             tag: 'a',
13006                             href: '#',
13007                             cls: 'btn',
13008                             cn: [
13009                                 {
13010                                     tag: 'span',
13011                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13012                                 }
13013                             ]
13014                         }
13015                     ]
13016                 },
13017                 {
13018                     tag: 'td',
13019                     cls: 'separator'
13020                 }
13021             ]
13022         });
13023         
13024     },
13025     
13026     update: function()
13027     {
13028         
13029         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13030         
13031         this.fill();
13032     },
13033     
13034     fill: function() 
13035     {
13036         var hours = this.time.getHours();
13037         var minutes = this.time.getMinutes();
13038         var period = 'AM';
13039         
13040         if(hours > 11){
13041             period = 'PM';
13042         }
13043         
13044         if(hours == 0){
13045             hours = 12;
13046         }
13047         
13048         
13049         if(hours > 12){
13050             hours = hours - 12;
13051         }
13052         
13053         if(hours < 10){
13054             hours = '0' + hours;
13055         }
13056         
13057         if(minutes < 10){
13058             minutes = '0' + minutes;
13059         }
13060         
13061         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13062         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13063         this.pop.select('button', true).first().dom.innerHTML = period;
13064         
13065     },
13066     
13067     place: function()
13068     {   
13069         this.picker().removeClass(['bottom', 'top']);
13070         
13071         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13072             /*
13073              * place to the top of element!
13074              *
13075              */
13076             
13077             this.picker().addClass('top');
13078             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13079             
13080             return;
13081         }
13082         
13083         this.picker().addClass('bottom');
13084         
13085         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13086     },
13087   
13088     onFocus : function()
13089     {
13090         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13091         this.show();
13092     },
13093     
13094     onBlur : function()
13095     {
13096         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13097         this.hide();
13098     },
13099     
13100     show : function()
13101     {
13102         this.picker().show();
13103         this.pop.show();
13104         this.update();
13105         this.place();
13106         
13107         this.fireEvent('show', this, this.date);
13108     },
13109     
13110     hide : function()
13111     {
13112         this.picker().hide();
13113         this.pop.hide();
13114         
13115         this.fireEvent('hide', this, this.date);
13116     },
13117     
13118     setTime : function()
13119     {
13120         this.hide();
13121         this.setValue(this.time.format(this.format));
13122         
13123         this.fireEvent('select', this, this.date);
13124         
13125         
13126     },
13127     
13128     onMousedown: function(e){
13129         e.stopPropagation();
13130         e.preventDefault();
13131     },
13132     
13133     onIncrementHours: function()
13134     {
13135         Roo.log('onIncrementHours');
13136         this.time = this.time.add(Date.HOUR, 1);
13137         this.update();
13138         
13139     },
13140     
13141     onDecrementHours: function()
13142     {
13143         Roo.log('onDecrementHours');
13144         this.time = this.time.add(Date.HOUR, -1);
13145         this.update();
13146     },
13147     
13148     onIncrementMinutes: function()
13149     {
13150         Roo.log('onIncrementMinutes');
13151         this.time = this.time.add(Date.MINUTE, 1);
13152         this.update();
13153     },
13154     
13155     onDecrementMinutes: function()
13156     {
13157         Roo.log('onDecrementMinutes');
13158         this.time = this.time.add(Date.MINUTE, -1);
13159         this.update();
13160     },
13161     
13162     onTogglePeriod: function()
13163     {
13164         Roo.log('onTogglePeriod');
13165         this.time = this.time.add(Date.HOUR, 12);
13166         this.update();
13167     }
13168     
13169    
13170 });
13171
13172 Roo.apply(Roo.bootstrap.TimeField,  {
13173     
13174     content : {
13175         tag: 'tbody',
13176         cn: [
13177             {
13178                 tag: 'tr',
13179                 cn: [
13180                 {
13181                     tag: 'td',
13182                     colspan: '7'
13183                 }
13184                 ]
13185             }
13186         ]
13187     },
13188     
13189     footer : {
13190         tag: 'tfoot',
13191         cn: [
13192             {
13193                 tag: 'tr',
13194                 cn: [
13195                 {
13196                     tag: 'th',
13197                     colspan: '7',
13198                     cls: '',
13199                     cn: [
13200                         {
13201                             tag: 'button',
13202                             cls: 'btn btn-info ok',
13203                             html: 'OK'
13204                         }
13205                     ]
13206                 }
13207
13208                 ]
13209             }
13210         ]
13211     }
13212 });
13213
13214 Roo.apply(Roo.bootstrap.TimeField,  {
13215   
13216     template : {
13217         tag: 'div',
13218         cls: 'datepicker dropdown-menu',
13219         cn: [
13220             {
13221                 tag: 'div',
13222                 cls: 'datepicker-time',
13223                 cn: [
13224                 {
13225                     tag: 'table',
13226                     cls: 'table-condensed',
13227                     cn:[
13228                     Roo.bootstrap.TimeField.content,
13229                     Roo.bootstrap.TimeField.footer
13230                     ]
13231                 }
13232                 ]
13233             }
13234         ]
13235     }
13236 });
13237
13238  
13239
13240  /*
13241  * - LGPL
13242  *
13243  * CheckBox
13244  * 
13245  */
13246
13247 /**
13248  * @class Roo.bootstrap.CheckBox
13249  * @extends Roo.bootstrap.Input
13250  * Bootstrap CheckBox class
13251  * 
13252  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13253  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13254  * @cfg {String} boxLabel The text that appears beside the checkbox
13255  * @cfg {Boolean} checked initnal the element
13256  * 
13257  * @constructor
13258  * Create a new CheckBox
13259  * @param {Object} config The config object
13260  */
13261
13262 Roo.bootstrap.CheckBox = function(config){
13263     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13264    
13265         this.addEvents({
13266             /**
13267             * @event check
13268             * Fires when the element is checked or unchecked.
13269             * @param {Roo.bootstrap.CheckBox} this This input
13270             * @param {Boolean} checked The new checked value
13271             */
13272            check : true
13273         });
13274 };
13275
13276 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13277     
13278     inputType: 'checkbox',
13279     inputValue: 1,
13280     valueOff: 0,
13281     boxLabel: false,
13282     checked: false,
13283     
13284     getAutoCreate : function()
13285     {
13286         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13287         
13288         var id = Roo.id();
13289         
13290         var cfg = {};
13291         
13292         cfg.cls = 'form-group' //input-group
13293         
13294         var input =  {
13295             tag: 'input',
13296             id : id,
13297             type : this.inputType,
13298             value : (!this.checked) ? this.valueOff : this.inputValue,
13299             cls : 'form-box',
13300             placeholder : this.placeholder || ''
13301             
13302         };
13303         
13304         if (this.disabled) {
13305             input.disabled=true;
13306         }
13307         
13308         if(this.checked){
13309             input.checked = this.checked;
13310         }
13311         
13312         if (this.name) {
13313             input.name = this.name;
13314         }
13315         
13316         if (this.size) {
13317             input.cls += ' input-' + this.size;
13318         }
13319         
13320         var settings=this;
13321         ['xs','sm','md','lg'].map(function(size){
13322             if (settings[size]) {
13323                 cfg.cls += ' col-' + size + '-' + settings[size];
13324             }
13325         });
13326         
13327         var inputblock = input;
13328         
13329         if (this.before || this.after) {
13330             
13331             inputblock = {
13332                 cls : 'input-group',
13333                 cn :  [] 
13334             };
13335             if (this.before) {
13336                 inputblock.cn.push({
13337                     tag :'span',
13338                     cls : 'input-group-addon',
13339                     html : this.before
13340                 });
13341             }
13342             inputblock.cn.push(input);
13343             if (this.after) {
13344                 inputblock.cn.push({
13345                     tag :'span',
13346                     cls : 'input-group-addon',
13347                     html : this.after
13348                 });
13349             }
13350             
13351         };
13352         
13353         if (align ==='left' && this.fieldLabel.length) {
13354                 Roo.log("left and has label");
13355                 cfg.cn = [
13356                     
13357                     {
13358                         tag: 'label',
13359                         'for' :  id,
13360                         cls : 'control-label col-md-' + this.labelWidth,
13361                         html : this.fieldLabel
13362                         
13363                     },
13364                     {
13365                         cls : "col-md-" + (12 - this.labelWidth), 
13366                         cn: [
13367                             inputblock
13368                         ]
13369                     }
13370                     
13371                 ];
13372         } else if ( this.fieldLabel.length) {
13373                 Roo.log(" label");
13374                 cfg.cn = [
13375                    
13376                     {
13377                         tag: this.boxLabel ? 'span' : 'label',
13378                         'for': id,
13379                         cls: 'control-label box-input-label',
13380                         //cls : 'input-group-addon',
13381                         html : this.fieldLabel
13382                         
13383                     },
13384                     
13385                     inputblock
13386                     
13387                 ];
13388
13389         } else {
13390             
13391                    Roo.log(" no label && no align");
13392                 cfg.cn = [
13393                     
13394                         inputblock
13395                     
13396                 ];
13397                 
13398                 
13399         };
13400         
13401         if(this.boxLabel){
13402             cfg.cn.push({
13403                 tag: 'label',
13404                 'for': id,
13405                 cls: 'box-label',
13406                 html: this.boxLabel
13407             })
13408         }
13409         
13410         return cfg;
13411         
13412     },
13413     
13414     /**
13415      * return the real input element.
13416      */
13417     inputEl: function ()
13418     {
13419         return this.el.select('input.form-box',true).first();
13420     },
13421     
13422     label: function()
13423     {
13424         return this.el.select('label.control-label',true).first();
13425     },
13426     
13427     initEvents : function()
13428     {
13429 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13430         
13431         this.inputEl().on('click', this.onClick,  this);
13432         
13433     },
13434     
13435     onClick : function()
13436     {   
13437         this.setChecked(!this.checked);
13438     },
13439     
13440     setChecked : function(state,suppressEvent)
13441     {
13442         this.checked = state;
13443         
13444         this.inputEl().dom.checked = state;
13445         
13446         if(suppressEvent !== true){
13447             this.fireEvent('check', this, state);
13448         }
13449         
13450         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13451         
13452     },
13453     
13454     setValue : function(v,suppressEvent)
13455     {
13456         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13457     }
13458     
13459 });
13460
13461  
13462 /*
13463  * - LGPL
13464  *
13465  * Radio
13466  * 
13467  */
13468
13469 /**
13470  * @class Roo.bootstrap.Radio
13471  * @extends Roo.bootstrap.CheckBox
13472  * Bootstrap Radio class
13473
13474  * @constructor
13475  * Create a new Radio
13476  * @param {Object} config The config object
13477  */
13478
13479 Roo.bootstrap.Radio = function(config){
13480     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13481    
13482 };
13483
13484 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13485     
13486     inputType: 'radio',
13487     inputValue: '',
13488     valueOff: '',
13489     
13490     getAutoCreate : function()
13491     {
13492         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13493         
13494         var id = Roo.id();
13495         
13496         var cfg = {};
13497         
13498         cfg.cls = 'form-group' //input-group
13499         
13500         var input =  {
13501             tag: 'input',
13502             id : id,
13503             type : this.inputType,
13504             value : (!this.checked) ? this.valueOff : this.inputValue,
13505             cls : 'form-box',
13506             placeholder : this.placeholder || ''
13507             
13508         };
13509         
13510         if (this.disabled) {
13511             input.disabled=true;
13512         }
13513         
13514         if(this.checked){
13515             input.checked = this.checked;
13516         }
13517         
13518         if (this.name) {
13519             input.name = this.name;
13520         }
13521         
13522         if (this.size) {
13523             input.cls += ' input-' + this.size;
13524         }
13525         
13526         var settings=this;
13527         ['xs','sm','md','lg'].map(function(size){
13528             if (settings[size]) {
13529                 cfg.cls += ' col-' + size + '-' + settings[size];
13530             }
13531         });
13532         
13533         var inputblock = input;
13534         
13535         if (this.before || this.after) {
13536             
13537             inputblock = {
13538                 cls : 'input-group',
13539                 cn :  [] 
13540             };
13541             if (this.before) {
13542                 inputblock.cn.push({
13543                     tag :'span',
13544                     cls : 'input-group-addon',
13545                     html : this.before
13546                 });
13547             }
13548             inputblock.cn.push(input);
13549             if (this.after) {
13550                 inputblock.cn.push({
13551                     tag :'span',
13552                     cls : 'input-group-addon',
13553                     html : this.after
13554                 });
13555             }
13556             
13557         };
13558         
13559         if (align ==='left' && this.fieldLabel.length) {
13560                 Roo.log("left and has label");
13561                 cfg.cn = [
13562                     
13563                     {
13564                         tag: 'label',
13565                         'for' :  id,
13566                         cls : 'control-label col-md-' + this.labelWidth,
13567                         html : this.fieldLabel
13568                         
13569                     },
13570                     {
13571                         cls : "col-md-" + (12 - this.labelWidth), 
13572                         cn: [
13573                             inputblock
13574                         ]
13575                     }
13576                     
13577                 ];
13578         } else if ( this.fieldLabel.length) {
13579                 Roo.log(" label");
13580                  cfg.cn = [
13581                    
13582                     {
13583                         tag: 'label',
13584                         'for': id,
13585                         cls: 'control-label box-input-label',
13586                         //cls : 'input-group-addon',
13587                         html : this.fieldLabel
13588                         
13589                     },
13590                     
13591                     inputblock
13592                     
13593                 ];
13594
13595         } else {
13596             
13597                    Roo.log(" no label && no align");
13598                 cfg.cn = [
13599                     
13600                         inputblock
13601                     
13602                 ];
13603                 
13604                 
13605         };
13606         
13607         if(this.boxLabel){
13608             cfg.cn.push({
13609                 tag: 'label',
13610                 'for': id,
13611                 cls: 'box-label',
13612                 html: this.boxLabel
13613             })
13614         }
13615         
13616         return cfg;
13617         
13618     },
13619    
13620     onClick : function()
13621     {   
13622         this.setChecked(true);
13623     },
13624     
13625     setChecked : function(state,suppressEvent)
13626     {
13627         if(state){
13628             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13629                 v.dom.checked = false;
13630             });
13631         }
13632         
13633         this.checked = state;
13634         this.inputEl().dom.checked = state;
13635         
13636         if(suppressEvent !== true){
13637             this.fireEvent('check', this, state);
13638         }
13639         
13640         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13641         
13642     },
13643     
13644     getGroupValue : function()
13645     {
13646         var value = ''
13647         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13648             if(v.dom.checked == true){
13649                 value = v.dom.value;
13650             }
13651         });
13652         
13653         return value;
13654     },
13655     
13656     /**
13657      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13658      * @return {Mixed} value The field value
13659      */
13660     getValue : function(){
13661         return this.getGroupValue();
13662     }
13663     
13664 });
13665
13666  
13667 //<script type="text/javascript">
13668
13669 /*
13670  * Based  Ext JS Library 1.1.1
13671  * Copyright(c) 2006-2007, Ext JS, LLC.
13672  * LGPL
13673  *
13674  */
13675  
13676 /**
13677  * @class Roo.HtmlEditorCore
13678  * @extends Roo.Component
13679  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13680  *
13681  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13682  */
13683
13684 Roo.HtmlEditorCore = function(config){
13685     
13686     
13687     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13688     this.addEvents({
13689         /**
13690          * @event initialize
13691          * Fires when the editor is fully initialized (including the iframe)
13692          * @param {Roo.HtmlEditorCore} this
13693          */
13694         initialize: true,
13695         /**
13696          * @event activate
13697          * Fires when the editor is first receives the focus. Any insertion must wait
13698          * until after this event.
13699          * @param {Roo.HtmlEditorCore} this
13700          */
13701         activate: true,
13702          /**
13703          * @event beforesync
13704          * Fires before the textarea is updated with content from the editor iframe. Return false
13705          * to cancel the sync.
13706          * @param {Roo.HtmlEditorCore} this
13707          * @param {String} html
13708          */
13709         beforesync: true,
13710          /**
13711          * @event beforepush
13712          * Fires before the iframe editor is updated with content from the textarea. Return false
13713          * to cancel the push.
13714          * @param {Roo.HtmlEditorCore} this
13715          * @param {String} html
13716          */
13717         beforepush: true,
13718          /**
13719          * @event sync
13720          * Fires when the textarea is updated with content from the editor iframe.
13721          * @param {Roo.HtmlEditorCore} this
13722          * @param {String} html
13723          */
13724         sync: true,
13725          /**
13726          * @event push
13727          * Fires when the iframe editor is updated with content from the textarea.
13728          * @param {Roo.HtmlEditorCore} this
13729          * @param {String} html
13730          */
13731         push: true,
13732         
13733         /**
13734          * @event editorevent
13735          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13736          * @param {Roo.HtmlEditorCore} this
13737          */
13738         editorevent: true
13739     });
13740      
13741 };
13742
13743
13744 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
13745
13746
13747      /**
13748      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
13749      */
13750     
13751     owner : false,
13752     
13753      /**
13754      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13755      *                        Roo.resizable.
13756      */
13757     resizable : false,
13758      /**
13759      * @cfg {Number} height (in pixels)
13760      */   
13761     height: 300,
13762    /**
13763      * @cfg {Number} width (in pixels)
13764      */   
13765     width: 500,
13766     
13767     /**
13768      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13769      * 
13770      */
13771     stylesheets: false,
13772     
13773     // id of frame..
13774     frameId: false,
13775     
13776     // private properties
13777     validationEvent : false,
13778     deferHeight: true,
13779     initialized : false,
13780     activated : false,
13781     sourceEditMode : false,
13782     onFocus : Roo.emptyFn,
13783     iframePad:3,
13784     hideMode:'offsets',
13785     
13786     clearUp: true,
13787     
13788      
13789     
13790
13791     /**
13792      * Protected method that will not generally be called directly. It
13793      * is called when the editor initializes the iframe with HTML contents. Override this method if you
13794      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13795      */
13796     getDocMarkup : function(){
13797         // body styles..
13798         var st = '';
13799         Roo.log(this.stylesheets);
13800         
13801         // inherit styels from page...?? 
13802         if (this.stylesheets === false) {
13803             
13804             Roo.get(document.head).select('style').each(function(node) {
13805                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13806             });
13807             
13808             Roo.get(document.head).select('link').each(function(node) { 
13809                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13810             });
13811             
13812         } else if (!this.stylesheets.length) {
13813                 // simple..
13814                 st = '<style type="text/css">' +
13815                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13816                    '</style>';
13817         } else {
13818             Roo.each(this.stylesheets, function(s) {
13819                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13820             });
13821             
13822         }
13823         
13824         st +=  '<style type="text/css">' +
13825             'IMG { cursor: pointer } ' +
13826         '</style>';
13827
13828         
13829         return '<html><head>' + st  +
13830             //<style type="text/css">' +
13831             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13832             //'</style>' +
13833             ' </head><body class="roo-htmleditor-body"></body></html>';
13834     },
13835
13836     // private
13837     onRender : function(ct, position)
13838     {
13839         var _t = this;
13840         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13841         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13842         
13843         
13844         this.el.dom.style.border = '0 none';
13845         this.el.dom.setAttribute('tabIndex', -1);
13846         this.el.addClass('x-hidden hide');
13847         
13848         
13849         
13850         if(Roo.isIE){ // fix IE 1px bogus margin
13851             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13852         }
13853        
13854         
13855         this.frameId = Roo.id();
13856         
13857          
13858         
13859         var iframe = this.owner.wrap.createChild({
13860             tag: 'iframe',
13861             cls: 'form-control', // bootstrap..
13862             id: this.frameId,
13863             name: this.frameId,
13864             frameBorder : 'no',
13865             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13866         }, this.el
13867         );
13868         
13869         
13870         this.iframe = iframe.dom;
13871
13872          this.assignDocWin();
13873         
13874         this.doc.designMode = 'on';
13875        
13876         this.doc.open();
13877         this.doc.write(this.getDocMarkup());
13878         this.doc.close();
13879
13880         
13881         var task = { // must defer to wait for browser to be ready
13882             run : function(){
13883                 //console.log("run task?" + this.doc.readyState);
13884                 this.assignDocWin();
13885                 if(this.doc.body || this.doc.readyState == 'complete'){
13886                     try {
13887                         this.doc.designMode="on";
13888                     } catch (e) {
13889                         return;
13890                     }
13891                     Roo.TaskMgr.stop(task);
13892                     this.initEditor.defer(10, this);
13893                 }
13894             },
13895             interval : 10,
13896             duration: 10000,
13897             scope: this
13898         };
13899         Roo.TaskMgr.start(task);
13900
13901         
13902          
13903     },
13904
13905     // private
13906     onResize : function(w, h)
13907     {
13908          Roo.log('resize: ' +w + ',' + h );
13909         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13910         if(!this.iframe){
13911             return;
13912         }
13913         if(typeof w == 'number'){
13914             
13915             this.iframe.style.width = w + 'px';
13916         }
13917         if(typeof h == 'number'){
13918             
13919             this.iframe.style.height = h + 'px';
13920             if(this.doc){
13921                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13922             }
13923         }
13924         
13925     },
13926
13927     /**
13928      * Toggles the editor between standard and source edit mode.
13929      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13930      */
13931     toggleSourceEdit : function(sourceEditMode){
13932         
13933         this.sourceEditMode = sourceEditMode === true;
13934         
13935         if(this.sourceEditMode){
13936  
13937             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13938             
13939         }else{
13940             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13941             //this.iframe.className = '';
13942             this.deferFocus();
13943         }
13944         //this.setSize(this.owner.wrap.getSize());
13945         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13946     },
13947
13948     
13949   
13950
13951     /**
13952      * Protected method that will not generally be called directly. If you need/want
13953      * custom HTML cleanup, this is the method you should override.
13954      * @param {String} html The HTML to be cleaned
13955      * return {String} The cleaned HTML
13956      */
13957     cleanHtml : function(html){
13958         html = String(html);
13959         if(html.length > 5){
13960             if(Roo.isSafari){ // strip safari nonsense
13961                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13962             }
13963         }
13964         if(html == '&nbsp;'){
13965             html = '';
13966         }
13967         return html;
13968     },
13969
13970     /**
13971      * HTML Editor -> Textarea
13972      * Protected method that will not generally be called directly. Syncs the contents
13973      * of the editor iframe with the textarea.
13974      */
13975     syncValue : function(){
13976         if(this.initialized){
13977             var bd = (this.doc.body || this.doc.documentElement);
13978             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13979             var html = bd.innerHTML;
13980             if(Roo.isSafari){
13981                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13982                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13983                 if(m && m[1]){
13984                     html = '<div style="'+m[0]+'">' + html + '</div>';
13985                 }
13986             }
13987             html = this.cleanHtml(html);
13988             // fix up the special chars.. normaly like back quotes in word...
13989             // however we do not want to do this with chinese..
13990             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13991                 var cc = b.charCodeAt();
13992                 if (
13993                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13994                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13995                     (cc >= 0xf900 && cc < 0xfb00 )
13996                 ) {
13997                         return b;
13998                 }
13999                 return "&#"+cc+";" 
14000             });
14001             if(this.owner.fireEvent('beforesync', this, html) !== false){
14002                 this.el.dom.value = html;
14003                 this.owner.fireEvent('sync', this, html);
14004             }
14005         }
14006     },
14007
14008     /**
14009      * Protected method that will not generally be called directly. Pushes the value of the textarea
14010      * into the iframe editor.
14011      */
14012     pushValue : function(){
14013         if(this.initialized){
14014             var v = this.el.dom.value.trim();
14015             
14016 //            if(v.length < 1){
14017 //                v = '&#160;';
14018 //            }
14019             
14020             if(this.owner.fireEvent('beforepush', this, v) !== false){
14021                 var d = (this.doc.body || this.doc.documentElement);
14022                 d.innerHTML = v;
14023                 this.cleanUpPaste();
14024                 this.el.dom.value = d.innerHTML;
14025                 this.owner.fireEvent('push', this, v);
14026             }
14027         }
14028     },
14029
14030     // private
14031     deferFocus : function(){
14032         this.focus.defer(10, this);
14033     },
14034
14035     // doc'ed in Field
14036     focus : function(){
14037         if(this.win && !this.sourceEditMode){
14038             this.win.focus();
14039         }else{
14040             this.el.focus();
14041         }
14042     },
14043     
14044     assignDocWin: function()
14045     {
14046         var iframe = this.iframe;
14047         
14048          if(Roo.isIE){
14049             this.doc = iframe.contentWindow.document;
14050             this.win = iframe.contentWindow;
14051         } else {
14052             if (!Roo.get(this.frameId)) {
14053                 return;
14054             }
14055             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14056             this.win = Roo.get(this.frameId).dom.contentWindow;
14057         }
14058     },
14059     
14060     // private
14061     initEditor : function(){
14062         //console.log("INIT EDITOR");
14063         this.assignDocWin();
14064         
14065         
14066         
14067         this.doc.designMode="on";
14068         this.doc.open();
14069         this.doc.write(this.getDocMarkup());
14070         this.doc.close();
14071         
14072         var dbody = (this.doc.body || this.doc.documentElement);
14073         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14074         // this copies styles from the containing element into thsi one..
14075         // not sure why we need all of this..
14076         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14077         ss['background-attachment'] = 'fixed'; // w3c
14078         dbody.bgProperties = 'fixed'; // ie
14079         Roo.DomHelper.applyStyles(dbody, ss);
14080         Roo.EventManager.on(this.doc, {
14081             //'mousedown': this.onEditorEvent,
14082             'mouseup': this.onEditorEvent,
14083             'dblclick': this.onEditorEvent,
14084             'click': this.onEditorEvent,
14085             'keyup': this.onEditorEvent,
14086             buffer:100,
14087             scope: this
14088         });
14089         if(Roo.isGecko){
14090             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14091         }
14092         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14093             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14094         }
14095         this.initialized = true;
14096
14097         this.owner.fireEvent('initialize', this);
14098         this.pushValue();
14099     },
14100
14101     // private
14102     onDestroy : function(){
14103         
14104         
14105         
14106         if(this.rendered){
14107             
14108             //for (var i =0; i < this.toolbars.length;i++) {
14109             //    // fixme - ask toolbars for heights?
14110             //    this.toolbars[i].onDestroy();
14111            // }
14112             
14113             //this.wrap.dom.innerHTML = '';
14114             //this.wrap.remove();
14115         }
14116     },
14117
14118     // private
14119     onFirstFocus : function(){
14120         
14121         this.assignDocWin();
14122         
14123         
14124         this.activated = true;
14125          
14126     
14127         if(Roo.isGecko){ // prevent silly gecko errors
14128             this.win.focus();
14129             var s = this.win.getSelection();
14130             if(!s.focusNode || s.focusNode.nodeType != 3){
14131                 var r = s.getRangeAt(0);
14132                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14133                 r.collapse(true);
14134                 this.deferFocus();
14135             }
14136             try{
14137                 this.execCmd('useCSS', true);
14138                 this.execCmd('styleWithCSS', false);
14139             }catch(e){}
14140         }
14141         this.owner.fireEvent('activate', this);
14142     },
14143
14144     // private
14145     adjustFont: function(btn){
14146         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14147         //if(Roo.isSafari){ // safari
14148         //    adjust *= 2;
14149        // }
14150         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14151         if(Roo.isSafari){ // safari
14152             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14153             v =  (v < 10) ? 10 : v;
14154             v =  (v > 48) ? 48 : v;
14155             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14156             
14157         }
14158         
14159         
14160         v = Math.max(1, v+adjust);
14161         
14162         this.execCmd('FontSize', v  );
14163     },
14164
14165     onEditorEvent : function(e){
14166         this.owner.fireEvent('editorevent', this, e);
14167       //  this.updateToolbar();
14168         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14169     },
14170
14171     insertTag : function(tg)
14172     {
14173         // could be a bit smarter... -> wrap the current selected tRoo..
14174         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14175             
14176             range = this.createRange(this.getSelection());
14177             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14178             wrappingNode.appendChild(range.extractContents());
14179             range.insertNode(wrappingNode);
14180
14181             return;
14182             
14183             
14184             
14185         }
14186         this.execCmd("formatblock",   tg);
14187         
14188     },
14189     
14190     insertText : function(txt)
14191     {
14192         
14193         
14194         var range = this.createRange();
14195         range.deleteContents();
14196                //alert(Sender.getAttribute('label'));
14197                
14198         range.insertNode(this.doc.createTextNode(txt));
14199     } ,
14200     
14201      
14202
14203     /**
14204      * Executes a Midas editor command on the editor document and performs necessary focus and
14205      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14206      * @param {String} cmd The Midas command
14207      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14208      */
14209     relayCmd : function(cmd, value){
14210         this.win.focus();
14211         this.execCmd(cmd, value);
14212         this.owner.fireEvent('editorevent', this);
14213         //this.updateToolbar();
14214         this.owner.deferFocus();
14215     },
14216
14217     /**
14218      * Executes a Midas editor command directly on the editor document.
14219      * For visual commands, you should use {@link #relayCmd} instead.
14220      * <b>This should only be called after the editor is initialized.</b>
14221      * @param {String} cmd The Midas command
14222      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14223      */
14224     execCmd : function(cmd, value){
14225         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14226         this.syncValue();
14227     },
14228  
14229  
14230    
14231     /**
14232      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14233      * to insert tRoo.
14234      * @param {String} text | dom node.. 
14235      */
14236     insertAtCursor : function(text)
14237     {
14238         
14239         
14240         
14241         if(!this.activated){
14242             return;
14243         }
14244         /*
14245         if(Roo.isIE){
14246             this.win.focus();
14247             var r = this.doc.selection.createRange();
14248             if(r){
14249                 r.collapse(true);
14250                 r.pasteHTML(text);
14251                 this.syncValue();
14252                 this.deferFocus();
14253             
14254             }
14255             return;
14256         }
14257         */
14258         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14259             this.win.focus();
14260             
14261             
14262             // from jquery ui (MIT licenced)
14263             var range, node;
14264             var win = this.win;
14265             
14266             if (win.getSelection && win.getSelection().getRangeAt) {
14267                 range = win.getSelection().getRangeAt(0);
14268                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14269                 range.insertNode(node);
14270             } else if (win.document.selection && win.document.selection.createRange) {
14271                 // no firefox support
14272                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14273                 win.document.selection.createRange().pasteHTML(txt);
14274             } else {
14275                 // no firefox support
14276                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14277                 this.execCmd('InsertHTML', txt);
14278             } 
14279             
14280             this.syncValue();
14281             
14282             this.deferFocus();
14283         }
14284     },
14285  // private
14286     mozKeyPress : function(e){
14287         if(e.ctrlKey){
14288             var c = e.getCharCode(), cmd;
14289           
14290             if(c > 0){
14291                 c = String.fromCharCode(c).toLowerCase();
14292                 switch(c){
14293                     case 'b':
14294                         cmd = 'bold';
14295                         break;
14296                     case 'i':
14297                         cmd = 'italic';
14298                         break;
14299                     
14300                     case 'u':
14301                         cmd = 'underline';
14302                         break;
14303                     
14304                     case 'v':
14305                         this.cleanUpPaste.defer(100, this);
14306                         return;
14307                         
14308                 }
14309                 if(cmd){
14310                     this.win.focus();
14311                     this.execCmd(cmd);
14312                     this.deferFocus();
14313                     e.preventDefault();
14314                 }
14315                 
14316             }
14317         }
14318     },
14319
14320     // private
14321     fixKeys : function(){ // load time branching for fastest keydown performance
14322         if(Roo.isIE){
14323             return function(e){
14324                 var k = e.getKey(), r;
14325                 if(k == e.TAB){
14326                     e.stopEvent();
14327                     r = this.doc.selection.createRange();
14328                     if(r){
14329                         r.collapse(true);
14330                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14331                         this.deferFocus();
14332                     }
14333                     return;
14334                 }
14335                 
14336                 if(k == e.ENTER){
14337                     r = this.doc.selection.createRange();
14338                     if(r){
14339                         var target = r.parentElement();
14340                         if(!target || target.tagName.toLowerCase() != 'li'){
14341                             e.stopEvent();
14342                             r.pasteHTML('<br />');
14343                             r.collapse(false);
14344                             r.select();
14345                         }
14346                     }
14347                 }
14348                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14349                     this.cleanUpPaste.defer(100, this);
14350                     return;
14351                 }
14352                 
14353                 
14354             };
14355         }else if(Roo.isOpera){
14356             return function(e){
14357                 var k = e.getKey();
14358                 if(k == e.TAB){
14359                     e.stopEvent();
14360                     this.win.focus();
14361                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14362                     this.deferFocus();
14363                 }
14364                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14365                     this.cleanUpPaste.defer(100, this);
14366                     return;
14367                 }
14368                 
14369             };
14370         }else if(Roo.isSafari){
14371             return function(e){
14372                 var k = e.getKey();
14373                 
14374                 if(k == e.TAB){
14375                     e.stopEvent();
14376                     this.execCmd('InsertText','\t');
14377                     this.deferFocus();
14378                     return;
14379                 }
14380                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14381                     this.cleanUpPaste.defer(100, this);
14382                     return;
14383                 }
14384                 
14385              };
14386         }
14387     }(),
14388     
14389     getAllAncestors: function()
14390     {
14391         var p = this.getSelectedNode();
14392         var a = [];
14393         if (!p) {
14394             a.push(p); // push blank onto stack..
14395             p = this.getParentElement();
14396         }
14397         
14398         
14399         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14400             a.push(p);
14401             p = p.parentNode;
14402         }
14403         a.push(this.doc.body);
14404         return a;
14405     },
14406     lastSel : false,
14407     lastSelNode : false,
14408     
14409     
14410     getSelection : function() 
14411     {
14412         this.assignDocWin();
14413         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14414     },
14415     
14416     getSelectedNode: function() 
14417     {
14418         // this may only work on Gecko!!!
14419         
14420         // should we cache this!!!!
14421         
14422         
14423         
14424          
14425         var range = this.createRange(this.getSelection()).cloneRange();
14426         
14427         if (Roo.isIE) {
14428             var parent = range.parentElement();
14429             while (true) {
14430                 var testRange = range.duplicate();
14431                 testRange.moveToElementText(parent);
14432                 if (testRange.inRange(range)) {
14433                     break;
14434                 }
14435                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14436                     break;
14437                 }
14438                 parent = parent.parentElement;
14439             }
14440             return parent;
14441         }
14442         
14443         // is ancestor a text element.
14444         var ac =  range.commonAncestorContainer;
14445         if (ac.nodeType == 3) {
14446             ac = ac.parentNode;
14447         }
14448         
14449         var ar = ac.childNodes;
14450          
14451         var nodes = [];
14452         var other_nodes = [];
14453         var has_other_nodes = false;
14454         for (var i=0;i<ar.length;i++) {
14455             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14456                 continue;
14457             }
14458             // fullly contained node.
14459             
14460             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14461                 nodes.push(ar[i]);
14462                 continue;
14463             }
14464             
14465             // probably selected..
14466             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14467                 other_nodes.push(ar[i]);
14468                 continue;
14469             }
14470             // outer..
14471             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14472                 continue;
14473             }
14474             
14475             
14476             has_other_nodes = true;
14477         }
14478         if (!nodes.length && other_nodes.length) {
14479             nodes= other_nodes;
14480         }
14481         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14482             return false;
14483         }
14484         
14485         return nodes[0];
14486     },
14487     createRange: function(sel)
14488     {
14489         // this has strange effects when using with 
14490         // top toolbar - not sure if it's a great idea.
14491         //this.editor.contentWindow.focus();
14492         if (typeof sel != "undefined") {
14493             try {
14494                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14495             } catch(e) {
14496                 return this.doc.createRange();
14497             }
14498         } else {
14499             return this.doc.createRange();
14500         }
14501     },
14502     getParentElement: function()
14503     {
14504         
14505         this.assignDocWin();
14506         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14507         
14508         var range = this.createRange(sel);
14509          
14510         try {
14511             var p = range.commonAncestorContainer;
14512             while (p.nodeType == 3) { // text node
14513                 p = p.parentNode;
14514             }
14515             return p;
14516         } catch (e) {
14517             return null;
14518         }
14519     
14520     },
14521     /***
14522      *
14523      * Range intersection.. the hard stuff...
14524      *  '-1' = before
14525      *  '0' = hits..
14526      *  '1' = after.
14527      *         [ -- selected range --- ]
14528      *   [fail]                        [fail]
14529      *
14530      *    basically..
14531      *      if end is before start or  hits it. fail.
14532      *      if start is after end or hits it fail.
14533      *
14534      *   if either hits (but other is outside. - then it's not 
14535      *   
14536      *    
14537      **/
14538     
14539     
14540     // @see http://www.thismuchiknow.co.uk/?p=64.
14541     rangeIntersectsNode : function(range, node)
14542     {
14543         var nodeRange = node.ownerDocument.createRange();
14544         try {
14545             nodeRange.selectNode(node);
14546         } catch (e) {
14547             nodeRange.selectNodeContents(node);
14548         }
14549     
14550         var rangeStartRange = range.cloneRange();
14551         rangeStartRange.collapse(true);
14552     
14553         var rangeEndRange = range.cloneRange();
14554         rangeEndRange.collapse(false);
14555     
14556         var nodeStartRange = nodeRange.cloneRange();
14557         nodeStartRange.collapse(true);
14558     
14559         var nodeEndRange = nodeRange.cloneRange();
14560         nodeEndRange.collapse(false);
14561     
14562         return rangeStartRange.compareBoundaryPoints(
14563                  Range.START_TO_START, nodeEndRange) == -1 &&
14564                rangeEndRange.compareBoundaryPoints(
14565                  Range.START_TO_START, nodeStartRange) == 1;
14566         
14567          
14568     },
14569     rangeCompareNode : function(range, node)
14570     {
14571         var nodeRange = node.ownerDocument.createRange();
14572         try {
14573             nodeRange.selectNode(node);
14574         } catch (e) {
14575             nodeRange.selectNodeContents(node);
14576         }
14577         
14578         
14579         range.collapse(true);
14580     
14581         nodeRange.collapse(true);
14582      
14583         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14584         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
14585          
14586         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14587         
14588         var nodeIsBefore   =  ss == 1;
14589         var nodeIsAfter    = ee == -1;
14590         
14591         if (nodeIsBefore && nodeIsAfter)
14592             return 0; // outer
14593         if (!nodeIsBefore && nodeIsAfter)
14594             return 1; //right trailed.
14595         
14596         if (nodeIsBefore && !nodeIsAfter)
14597             return 2;  // left trailed.
14598         // fully contined.
14599         return 3;
14600     },
14601
14602     // private? - in a new class?
14603     cleanUpPaste :  function()
14604     {
14605         // cleans up the whole document..
14606         Roo.log('cleanuppaste');
14607         
14608         this.cleanUpChildren(this.doc.body);
14609         var clean = this.cleanWordChars(this.doc.body.innerHTML);
14610         if (clean != this.doc.body.innerHTML) {
14611             this.doc.body.innerHTML = clean;
14612         }
14613         
14614     },
14615     
14616     cleanWordChars : function(input) {// change the chars to hex code
14617         var he = Roo.HtmlEditorCore;
14618         
14619         var output = input;
14620         Roo.each(he.swapCodes, function(sw) { 
14621             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14622             
14623             output = output.replace(swapper, sw[1]);
14624         });
14625         
14626         return output;
14627     },
14628     
14629     
14630     cleanUpChildren : function (n)
14631     {
14632         if (!n.childNodes.length) {
14633             return;
14634         }
14635         for (var i = n.childNodes.length-1; i > -1 ; i--) {
14636            this.cleanUpChild(n.childNodes[i]);
14637         }
14638     },
14639     
14640     
14641         
14642     
14643     cleanUpChild : function (node)
14644     {
14645         var ed = this;
14646         //console.log(node);
14647         if (node.nodeName == "#text") {
14648             // clean up silly Windows -- stuff?
14649             return; 
14650         }
14651         if (node.nodeName == "#comment") {
14652             node.parentNode.removeChild(node);
14653             // clean up silly Windows -- stuff?
14654             return; 
14655         }
14656         
14657         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14658             // remove node.
14659             node.parentNode.removeChild(node);
14660             return;
14661             
14662         }
14663         
14664         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14665         
14666         // remove <a name=....> as rendering on yahoo mailer is borked with this.
14667         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14668         
14669         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14670         //    remove_keep_children = true;
14671         //}
14672         
14673         if (remove_keep_children) {
14674             this.cleanUpChildren(node);
14675             // inserts everything just before this node...
14676             while (node.childNodes.length) {
14677                 var cn = node.childNodes[0];
14678                 node.removeChild(cn);
14679                 node.parentNode.insertBefore(cn, node);
14680             }
14681             node.parentNode.removeChild(node);
14682             return;
14683         }
14684         
14685         if (!node.attributes || !node.attributes.length) {
14686             this.cleanUpChildren(node);
14687             return;
14688         }
14689         
14690         function cleanAttr(n,v)
14691         {
14692             
14693             if (v.match(/^\./) || v.match(/^\//)) {
14694                 return;
14695             }
14696             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14697                 return;
14698             }
14699             if (v.match(/^#/)) {
14700                 return;
14701             }
14702 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14703             node.removeAttribute(n);
14704             
14705         }
14706         
14707         function cleanStyle(n,v)
14708         {
14709             if (v.match(/expression/)) { //XSS?? should we even bother..
14710                 node.removeAttribute(n);
14711                 return;
14712             }
14713             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14714             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14715             
14716             
14717             var parts = v.split(/;/);
14718             var clean = [];
14719             
14720             Roo.each(parts, function(p) {
14721                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14722                 if (!p.length) {
14723                     return true;
14724                 }
14725                 var l = p.split(':').shift().replace(/\s+/g,'');
14726                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14727                 
14728                 if ( cblack.indexOf(l) > -1) {
14729 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14730                     //node.removeAttribute(n);
14731                     return true;
14732                 }
14733                 //Roo.log()
14734                 // only allow 'c whitelisted system attributes'
14735                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
14736 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14737                     //node.removeAttribute(n);
14738                     return true;
14739                 }
14740                 
14741                 
14742                  
14743                 
14744                 clean.push(p);
14745                 return true;
14746             });
14747             if (clean.length) { 
14748                 node.setAttribute(n, clean.join(';'));
14749             } else {
14750                 node.removeAttribute(n);
14751             }
14752             
14753         }
14754         
14755         
14756         for (var i = node.attributes.length-1; i > -1 ; i--) {
14757             var a = node.attributes[i];
14758             //console.log(a);
14759             
14760             if (a.name.toLowerCase().substr(0,2)=='on')  {
14761                 node.removeAttribute(a.name);
14762                 continue;
14763             }
14764             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14765                 node.removeAttribute(a.name);
14766                 continue;
14767             }
14768             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14769                 cleanAttr(a.name,a.value); // fixme..
14770                 continue;
14771             }
14772             if (a.name == 'style') {
14773                 cleanStyle(a.name,a.value);
14774                 continue;
14775             }
14776             /// clean up MS crap..
14777             // tecnically this should be a list of valid class'es..
14778             
14779             
14780             if (a.name == 'class') {
14781                 if (a.value.match(/^Mso/)) {
14782                     node.className = '';
14783                 }
14784                 
14785                 if (a.value.match(/body/)) {
14786                     node.className = '';
14787                 }
14788                 continue;
14789             }
14790             
14791             // style cleanup!?
14792             // class cleanup?
14793             
14794         }
14795         
14796         
14797         this.cleanUpChildren(node);
14798         
14799         
14800     }
14801     
14802     
14803     // hide stuff that is not compatible
14804     /**
14805      * @event blur
14806      * @hide
14807      */
14808     /**
14809      * @event change
14810      * @hide
14811      */
14812     /**
14813      * @event focus
14814      * @hide
14815      */
14816     /**
14817      * @event specialkey
14818      * @hide
14819      */
14820     /**
14821      * @cfg {String} fieldClass @hide
14822      */
14823     /**
14824      * @cfg {String} focusClass @hide
14825      */
14826     /**
14827      * @cfg {String} autoCreate @hide
14828      */
14829     /**
14830      * @cfg {String} inputType @hide
14831      */
14832     /**
14833      * @cfg {String} invalidClass @hide
14834      */
14835     /**
14836      * @cfg {String} invalidText @hide
14837      */
14838     /**
14839      * @cfg {String} msgFx @hide
14840      */
14841     /**
14842      * @cfg {String} validateOnBlur @hide
14843      */
14844 });
14845
14846 Roo.HtmlEditorCore.white = [
14847         'area', 'br', 'img', 'input', 'hr', 'wbr',
14848         
14849        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14850        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14851        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14852        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14853        'table',   'ul',         'xmp', 
14854        
14855        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14856       'thead',   'tr', 
14857      
14858       'dir', 'menu', 'ol', 'ul', 'dl',
14859        
14860       'embed',  'object'
14861 ];
14862
14863
14864 Roo.HtmlEditorCore.black = [
14865     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14866         'applet', // 
14867         'base',   'basefont', 'bgsound', 'blink',  'body', 
14868         'frame',  'frameset', 'head',    'html',   'ilayer', 
14869         'iframe', 'layer',  'link',     'meta',    'object',   
14870         'script', 'style' ,'title',  'xml' // clean later..
14871 ];
14872 Roo.HtmlEditorCore.clean = [
14873     'script', 'style', 'title', 'xml'
14874 ];
14875 Roo.HtmlEditorCore.remove = [
14876     'font'
14877 ];
14878 // attributes..
14879
14880 Roo.HtmlEditorCore.ablack = [
14881     'on'
14882 ];
14883     
14884 Roo.HtmlEditorCore.aclean = [ 
14885     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14886 ];
14887
14888 // protocols..
14889 Roo.HtmlEditorCore.pwhite= [
14890         'http',  'https',  'mailto'
14891 ];
14892
14893 // white listed style attributes.
14894 Roo.HtmlEditorCore.cwhite= [
14895       //  'text-align', /// default is to allow most things..
14896       
14897          
14898 //        'font-size'//??
14899 ];
14900
14901 // black listed style attributes.
14902 Roo.HtmlEditorCore.cblack= [
14903       //  'font-size' -- this can be set by the project 
14904 ];
14905
14906
14907 Roo.HtmlEditorCore.swapCodes   =[ 
14908     [    8211, "--" ], 
14909     [    8212, "--" ], 
14910     [    8216,  "'" ],  
14911     [    8217, "'" ],  
14912     [    8220, '"' ],  
14913     [    8221, '"' ],  
14914     [    8226, "*" ],  
14915     [    8230, "..." ]
14916 ]; 
14917
14918     /*
14919  * - LGPL
14920  *
14921  * HtmlEditor
14922  * 
14923  */
14924
14925 /**
14926  * @class Roo.bootstrap.HtmlEditor
14927  * @extends Roo.bootstrap.TextArea
14928  * Bootstrap HtmlEditor class
14929
14930  * @constructor
14931  * Create a new HtmlEditor
14932  * @param {Object} config The config object
14933  */
14934
14935 Roo.bootstrap.HtmlEditor = function(config){
14936     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14937     if (!this.toolbars) {
14938         this.toolbars = [];
14939     }
14940     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14941     this.addEvents({
14942             /**
14943              * @event initialize
14944              * Fires when the editor is fully initialized (including the iframe)
14945              * @param {HtmlEditor} this
14946              */
14947             initialize: true,
14948             /**
14949              * @event activate
14950              * Fires when the editor is first receives the focus. Any insertion must wait
14951              * until after this event.
14952              * @param {HtmlEditor} this
14953              */
14954             activate: true,
14955              /**
14956              * @event beforesync
14957              * Fires before the textarea is updated with content from the editor iframe. Return false
14958              * to cancel the sync.
14959              * @param {HtmlEditor} this
14960              * @param {String} html
14961              */
14962             beforesync: true,
14963              /**
14964              * @event beforepush
14965              * Fires before the iframe editor is updated with content from the textarea. Return false
14966              * to cancel the push.
14967              * @param {HtmlEditor} this
14968              * @param {String} html
14969              */
14970             beforepush: true,
14971              /**
14972              * @event sync
14973              * Fires when the textarea is updated with content from the editor iframe.
14974              * @param {HtmlEditor} this
14975              * @param {String} html
14976              */
14977             sync: true,
14978              /**
14979              * @event push
14980              * Fires when the iframe editor is updated with content from the textarea.
14981              * @param {HtmlEditor} this
14982              * @param {String} html
14983              */
14984             push: true,
14985              /**
14986              * @event editmodechange
14987              * Fires when the editor switches edit modes
14988              * @param {HtmlEditor} this
14989              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14990              */
14991             editmodechange: true,
14992             /**
14993              * @event editorevent
14994              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14995              * @param {HtmlEditor} this
14996              */
14997             editorevent: true,
14998             /**
14999              * @event firstfocus
15000              * Fires when on first focus - needed by toolbars..
15001              * @param {HtmlEditor} this
15002              */
15003             firstfocus: true,
15004             /**
15005              * @event autosave
15006              * Auto save the htmlEditor value as a file into Events
15007              * @param {HtmlEditor} this
15008              */
15009             autosave: true,
15010             /**
15011              * @event savedpreview
15012              * preview the saved version of htmlEditor
15013              * @param {HtmlEditor} this
15014              */
15015             savedpreview: true
15016         });
15017 };
15018
15019
15020 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15021     
15022     
15023       /**
15024      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15025      */
15026     toolbars : false,
15027    
15028      /**
15029      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15030      *                        Roo.resizable.
15031      */
15032     resizable : false,
15033      /**
15034      * @cfg {Number} height (in pixels)
15035      */   
15036     height: 300,
15037    /**
15038      * @cfg {Number} width (in pixels)
15039      */   
15040     width: false,
15041     
15042     /**
15043      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15044      * 
15045      */
15046     stylesheets: false,
15047     
15048     // id of frame..
15049     frameId: false,
15050     
15051     // private properties
15052     validationEvent : false,
15053     deferHeight: true,
15054     initialized : false,
15055     activated : false,
15056     
15057     onFocus : Roo.emptyFn,
15058     iframePad:3,
15059     hideMode:'offsets',
15060     
15061     
15062     tbContainer : false,
15063     
15064     toolbarContainer :function() {
15065         return this.wrap.select('.x-html-editor-tb',true).first();
15066     },
15067
15068     /**
15069      * Protected method that will not generally be called directly. It
15070      * is called when the editor creates its toolbar. Override this method if you need to
15071      * add custom toolbar buttons.
15072      * @param {HtmlEditor} editor
15073      */
15074     createToolbar : function(){
15075         
15076         Roo.log("create toolbars");
15077         
15078         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15079         this.toolbars[0].render(this.toolbarContainer());
15080         
15081         return;
15082         
15083 //        if (!editor.toolbars || !editor.toolbars.length) {
15084 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15085 //        }
15086 //        
15087 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15088 //            editor.toolbars[i] = Roo.factory(
15089 //                    typeof(editor.toolbars[i]) == 'string' ?
15090 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15091 //                Roo.bootstrap.HtmlEditor);
15092 //            editor.toolbars[i].init(editor);
15093 //        }
15094     },
15095
15096      
15097     // private
15098     onRender : function(ct, position)
15099     {
15100        // Roo.log("Call onRender: " + this.xtype);
15101         var _t = this;
15102         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15103       
15104         this.wrap = this.inputEl().wrap({
15105             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15106         });
15107         
15108         this.editorcore.onRender(ct, position);
15109          
15110         if (this.resizable) {
15111             this.resizeEl = new Roo.Resizable(this.wrap, {
15112                 pinned : true,
15113                 wrap: true,
15114                 dynamic : true,
15115                 minHeight : this.height,
15116                 height: this.height,
15117                 handles : this.resizable,
15118                 width: this.width,
15119                 listeners : {
15120                     resize : function(r, w, h) {
15121                         _t.onResize(w,h); // -something
15122                     }
15123                 }
15124             });
15125             
15126         }
15127         this.createToolbar(this);
15128        
15129         
15130         if(!this.width && this.resizable){
15131             this.setSize(this.wrap.getSize());
15132         }
15133         if (this.resizeEl) {
15134             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15135             // should trigger onReize..
15136         }
15137         
15138     },
15139
15140     // private
15141     onResize : function(w, h)
15142     {
15143         Roo.log('resize: ' +w + ',' + h );
15144         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15145         var ew = false;
15146         var eh = false;
15147         
15148         if(this.inputEl() ){
15149             if(typeof w == 'number'){
15150                 var aw = w - this.wrap.getFrameWidth('lr');
15151                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15152                 ew = aw;
15153             }
15154             if(typeof h == 'number'){
15155                  var tbh = -11;  // fixme it needs to tool bar size!
15156                 for (var i =0; i < this.toolbars.length;i++) {
15157                     // fixme - ask toolbars for heights?
15158                     tbh += this.toolbars[i].el.getHeight();
15159                     //if (this.toolbars[i].footer) {
15160                     //    tbh += this.toolbars[i].footer.el.getHeight();
15161                     //}
15162                 }
15163               
15164                 
15165                 
15166                 
15167                 
15168                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15169                 ah -= 5; // knock a few pixes off for look..
15170                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15171                 var eh = ah;
15172             }
15173         }
15174         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15175         this.editorcore.onResize(ew,eh);
15176         
15177     },
15178
15179     /**
15180      * Toggles the editor between standard and source edit mode.
15181      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15182      */
15183     toggleSourceEdit : function(sourceEditMode)
15184     {
15185         this.editorcore.toggleSourceEdit(sourceEditMode);
15186         
15187         if(this.editorcore.sourceEditMode){
15188             Roo.log('editor - showing textarea');
15189             
15190 //            Roo.log('in');
15191 //            Roo.log(this.syncValue());
15192             this.syncValue();
15193             this.inputEl().removeClass('hide');
15194             this.inputEl().dom.removeAttribute('tabIndex');
15195             this.inputEl().focus();
15196         }else{
15197             Roo.log('editor - hiding textarea');
15198 //            Roo.log('out')
15199 //            Roo.log(this.pushValue()); 
15200             this.pushValue();
15201             
15202             this.inputEl().addClass('hide');
15203             this.inputEl().dom.setAttribute('tabIndex', -1);
15204             //this.deferFocus();
15205         }
15206          
15207         if(this.resizable){
15208             this.setSize(this.wrap.getSize());
15209         }
15210         
15211         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15212     },
15213  
15214     // private (for BoxComponent)
15215     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15216
15217     // private (for BoxComponent)
15218     getResizeEl : function(){
15219         return this.wrap;
15220     },
15221
15222     // private (for BoxComponent)
15223     getPositionEl : function(){
15224         return this.wrap;
15225     },
15226
15227     // private
15228     initEvents : function(){
15229         this.originalValue = this.getValue();
15230     },
15231
15232 //    /**
15233 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15234 //     * @method
15235 //     */
15236 //    markInvalid : Roo.emptyFn,
15237 //    /**
15238 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15239 //     * @method
15240 //     */
15241 //    clearInvalid : Roo.emptyFn,
15242
15243     setValue : function(v){
15244         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15245         this.editorcore.pushValue();
15246     },
15247
15248      
15249     // private
15250     deferFocus : function(){
15251         this.focus.defer(10, this);
15252     },
15253
15254     // doc'ed in Field
15255     focus : function(){
15256         this.editorcore.focus();
15257         
15258     },
15259       
15260
15261     // private
15262     onDestroy : function(){
15263         
15264         
15265         
15266         if(this.rendered){
15267             
15268             for (var i =0; i < this.toolbars.length;i++) {
15269                 // fixme - ask toolbars for heights?
15270                 this.toolbars[i].onDestroy();
15271             }
15272             
15273             this.wrap.dom.innerHTML = '';
15274             this.wrap.remove();
15275         }
15276     },
15277
15278     // private
15279     onFirstFocus : function(){
15280         //Roo.log("onFirstFocus");
15281         this.editorcore.onFirstFocus();
15282          for (var i =0; i < this.toolbars.length;i++) {
15283             this.toolbars[i].onFirstFocus();
15284         }
15285         
15286     },
15287     
15288     // private
15289     syncValue : function()
15290     {   
15291         this.editorcore.syncValue();
15292     },
15293     
15294     pushValue : function()
15295     {   
15296         this.editorcore.pushValue();
15297     }
15298      
15299     
15300     // hide stuff that is not compatible
15301     /**
15302      * @event blur
15303      * @hide
15304      */
15305     /**
15306      * @event change
15307      * @hide
15308      */
15309     /**
15310      * @event focus
15311      * @hide
15312      */
15313     /**
15314      * @event specialkey
15315      * @hide
15316      */
15317     /**
15318      * @cfg {String} fieldClass @hide
15319      */
15320     /**
15321      * @cfg {String} focusClass @hide
15322      */
15323     /**
15324      * @cfg {String} autoCreate @hide
15325      */
15326     /**
15327      * @cfg {String} inputType @hide
15328      */
15329     /**
15330      * @cfg {String} invalidClass @hide
15331      */
15332     /**
15333      * @cfg {String} invalidText @hide
15334      */
15335     /**
15336      * @cfg {String} msgFx @hide
15337      */
15338     /**
15339      * @cfg {String} validateOnBlur @hide
15340      */
15341 });
15342  
15343     
15344    
15345    
15346    
15347       
15348
15349 /**
15350  * @class Roo.bootstrap.HtmlEditorToolbar1
15351  * Basic Toolbar
15352  * 
15353  * Usage:
15354  *
15355  new Roo.bootstrap.HtmlEditor({
15356     ....
15357     toolbars : [
15358         new Roo.bootstrap.HtmlEditorToolbar1({
15359             disable : { fonts: 1 , format: 1, ..., ... , ...],
15360             btns : [ .... ]
15361         })
15362     }
15363      
15364  * 
15365  * @cfg {Object} disable List of elements to disable..
15366  * @cfg {Array} btns List of additional buttons.
15367  * 
15368  * 
15369  * NEEDS Extra CSS? 
15370  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15371  */
15372  
15373 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15374 {
15375     
15376     Roo.apply(this, config);
15377     
15378     // default disabled, based on 'good practice'..
15379     this.disable = this.disable || {};
15380     Roo.applyIf(this.disable, {
15381         fontSize : true,
15382         colors : true,
15383         specialElements : true
15384     });
15385     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15386     
15387     this.editor = config.editor;
15388     this.editorcore = config.editor.editorcore;
15389     
15390     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15391     
15392     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15393     // dont call parent... till later.
15394 }
15395 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
15396     
15397     
15398     bar : true,
15399     
15400     editor : false,
15401     editorcore : false,
15402     
15403     
15404     formats : [
15405         "p" ,  
15406         "h1","h2","h3","h4","h5","h6", 
15407         "pre", "code", 
15408         "abbr", "acronym", "address", "cite", "samp", "var",
15409         'div','span'
15410     ],
15411     
15412     onRender : function(ct, position)
15413     {
15414        // Roo.log("Call onRender: " + this.xtype);
15415         
15416        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15417        Roo.log(this.el);
15418        this.el.dom.style.marginBottom = '0';
15419        var _this = this;
15420        var editorcore = this.editorcore;
15421        var editor= this.editor;
15422        
15423        var children = [];
15424        var btn = function(id,cmd , toggle, handler){
15425        
15426             var  event = toggle ? 'toggle' : 'click';
15427        
15428             var a = {
15429                 size : 'sm',
15430                 xtype: 'Button',
15431                 xns: Roo.bootstrap,
15432                 glyphicon : id,
15433                 cmd : id || cmd,
15434                 enableToggle:toggle !== false,
15435                 //html : 'submit'
15436                 pressed : toggle ? false : null,
15437                 listeners : {}
15438             }
15439             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15440                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15441             }
15442             children.push(a);
15443             return a;
15444        }
15445         
15446         var style = {
15447                 xtype: 'Button',
15448                 size : 'sm',
15449                 xns: Roo.bootstrap,
15450                 glyphicon : 'font',
15451                 //html : 'submit'
15452                 menu : {
15453                     xtype: 'Menu',
15454                     xns: Roo.bootstrap,
15455                     items:  []
15456                 }
15457         };
15458         Roo.each(this.formats, function(f) {
15459             style.menu.items.push({
15460                 xtype :'MenuItem',
15461                 xns: Roo.bootstrap,
15462                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15463                 tagname : f,
15464                 listeners : {
15465                     click : function()
15466                     {
15467                         editorcore.insertTag(this.tagname);
15468                         editor.focus();
15469                     }
15470                 }
15471                 
15472             });
15473         });
15474          children.push(style);   
15475             
15476             
15477         btn('bold',false,true);
15478         btn('italic',false,true);
15479         btn('align-left', 'justifyleft',true);
15480         btn('align-center', 'justifycenter',true);
15481         btn('align-right' , 'justifyright',true);
15482         btn('link', false, false, function(btn) {
15483             //Roo.log("create link?");
15484             var url = prompt(this.createLinkText, this.defaultLinkValue);
15485             if(url && url != 'http:/'+'/'){
15486                 this.editorcore.relayCmd('createlink', url);
15487             }
15488         }),
15489         btn('list','insertunorderedlist',true);
15490         btn('pencil', false,true, function(btn){
15491                 Roo.log(this);
15492                 
15493                 this.toggleSourceEdit(btn.pressed);
15494         });
15495         /*
15496         var cog = {
15497                 xtype: 'Button',
15498                 size : 'sm',
15499                 xns: Roo.bootstrap,
15500                 glyphicon : 'cog',
15501                 //html : 'submit'
15502                 menu : {
15503                     xtype: 'Menu',
15504                     xns: Roo.bootstrap,
15505                     items:  []
15506                 }
15507         };
15508         
15509         cog.menu.items.push({
15510             xtype :'MenuItem',
15511             xns: Roo.bootstrap,
15512             html : Clean styles,
15513             tagname : f,
15514             listeners : {
15515                 click : function()
15516                 {
15517                     editorcore.insertTag(this.tagname);
15518                     editor.focus();
15519                 }
15520             }
15521             
15522         });
15523        */
15524         
15525          
15526        this.xtype = 'Navbar';
15527         
15528         for(var i=0;i< children.length;i++) {
15529             
15530             this.buttons.add(this.addxtypeChild(children[i]));
15531             
15532         }
15533         
15534         editor.on('editorevent', this.updateToolbar, this);
15535     },
15536     onBtnClick : function(id)
15537     {
15538        this.editorcore.relayCmd(id);
15539        this.editorcore.focus();
15540     },
15541     
15542     /**
15543      * Protected method that will not generally be called directly. It triggers
15544      * a toolbar update by reading the markup state of the current selection in the editor.
15545      */
15546     updateToolbar: function(){
15547
15548         if(!this.editorcore.activated){
15549             this.editor.onFirstFocus(); // is this neeed?
15550             return;
15551         }
15552
15553         var btns = this.buttons; 
15554         var doc = this.editorcore.doc;
15555         btns.get('bold').setActive(doc.queryCommandState('bold'));
15556         btns.get('italic').setActive(doc.queryCommandState('italic'));
15557         //btns.get('underline').setActive(doc.queryCommandState('underline'));
15558         
15559         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15560         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15561         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15562         
15563         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15564         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15565          /*
15566         
15567         var ans = this.editorcore.getAllAncestors();
15568         if (this.formatCombo) {
15569             
15570             
15571             var store = this.formatCombo.store;
15572             this.formatCombo.setValue("");
15573             for (var i =0; i < ans.length;i++) {
15574                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15575                     // select it..
15576                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15577                     break;
15578                 }
15579             }
15580         }
15581         
15582         
15583         
15584         // hides menus... - so this cant be on a menu...
15585         Roo.bootstrap.MenuMgr.hideAll();
15586         */
15587         Roo.bootstrap.MenuMgr.hideAll();
15588         //this.editorsyncValue();
15589     },
15590     onFirstFocus: function() {
15591         this.buttons.each(function(item){
15592            item.enable();
15593         });
15594     },
15595     toggleSourceEdit : function(sourceEditMode){
15596         
15597           
15598         if(sourceEditMode){
15599             Roo.log("disabling buttons");
15600            this.buttons.each( function(item){
15601                 if(item.cmd != 'pencil'){
15602                     item.disable();
15603                 }
15604             });
15605           
15606         }else{
15607             Roo.log("enabling buttons");
15608             if(this.editorcore.initialized){
15609                 this.buttons.each( function(item){
15610                     item.enable();
15611                 });
15612             }
15613             
15614         }
15615         Roo.log("calling toggole on editor");
15616         // tell the editor that it's been pressed..
15617         this.editor.toggleSourceEdit(sourceEditMode);
15618        
15619     }
15620 });
15621
15622
15623
15624
15625
15626 /**
15627  * @class Roo.bootstrap.Table.AbstractSelectionModel
15628  * @extends Roo.util.Observable
15629  * Abstract base class for grid SelectionModels.  It provides the interface that should be
15630  * implemented by descendant classes.  This class should not be directly instantiated.
15631  * @constructor
15632  */
15633 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15634     this.locked = false;
15635     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15636 };
15637
15638
15639 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
15640     /** @ignore Called by the grid automatically. Do not call directly. */
15641     init : function(grid){
15642         this.grid = grid;
15643         this.initEvents();
15644     },
15645
15646     /**
15647      * Locks the selections.
15648      */
15649     lock : function(){
15650         this.locked = true;
15651     },
15652
15653     /**
15654      * Unlocks the selections.
15655      */
15656     unlock : function(){
15657         this.locked = false;
15658     },
15659
15660     /**
15661      * Returns true if the selections are locked.
15662      * @return {Boolean}
15663      */
15664     isLocked : function(){
15665         return this.locked;
15666     }
15667 });
15668 /**
15669  * @class Roo.bootstrap.Table.ColumnModel
15670  * @extends Roo.util.Observable
15671  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15672  * the columns in the table.
15673  
15674  * @constructor
15675  * @param {Object} config An Array of column config objects. See this class's
15676  * config objects for details.
15677 */
15678 Roo.bootstrap.Table.ColumnModel = function(config){
15679         /**
15680      * The config passed into the constructor
15681      */
15682     this.config = config;
15683     this.lookup = {};
15684
15685     // if no id, create one
15686     // if the column does not have a dataIndex mapping,
15687     // map it to the order it is in the config
15688     for(var i = 0, len = config.length; i < len; i++){
15689         var c = config[i];
15690         if(typeof c.dataIndex == "undefined"){
15691             c.dataIndex = i;
15692         }
15693         if(typeof c.renderer == "string"){
15694             c.renderer = Roo.util.Format[c.renderer];
15695         }
15696         if(typeof c.id == "undefined"){
15697             c.id = Roo.id();
15698         }
15699 //        if(c.editor && c.editor.xtype){
15700 //            c.editor  = Roo.factory(c.editor, Roo.grid);
15701 //        }
15702 //        if(c.editor && c.editor.isFormField){
15703 //            c.editor = new Roo.grid.GridEditor(c.editor);
15704 //        }
15705
15706         this.lookup[c.id] = c;
15707     }
15708
15709     /**
15710      * The width of columns which have no width specified (defaults to 100)
15711      * @type Number
15712      */
15713     this.defaultWidth = 100;
15714
15715     /**
15716      * Default sortable of columns which have no sortable specified (defaults to false)
15717      * @type Boolean
15718      */
15719     this.defaultSortable = false;
15720
15721     this.addEvents({
15722         /**
15723              * @event widthchange
15724              * Fires when the width of a column changes.
15725              * @param {ColumnModel} this
15726              * @param {Number} columnIndex The column index
15727              * @param {Number} newWidth The new width
15728              */
15729             "widthchange": true,
15730         /**
15731              * @event headerchange
15732              * Fires when the text of a header changes.
15733              * @param {ColumnModel} this
15734              * @param {Number} columnIndex The column index
15735              * @param {Number} newText The new header text
15736              */
15737             "headerchange": true,
15738         /**
15739              * @event hiddenchange
15740              * Fires when a column is hidden or "unhidden".
15741              * @param {ColumnModel} this
15742              * @param {Number} columnIndex The column index
15743              * @param {Boolean} hidden true if hidden, false otherwise
15744              */
15745             "hiddenchange": true,
15746             /**
15747          * @event columnmoved
15748          * Fires when a column is moved.
15749          * @param {ColumnModel} this
15750          * @param {Number} oldIndex
15751          * @param {Number} newIndex
15752          */
15753         "columnmoved" : true,
15754         /**
15755          * @event columlockchange
15756          * Fires when a column's locked state is changed
15757          * @param {ColumnModel} this
15758          * @param {Number} colIndex
15759          * @param {Boolean} locked true if locked
15760          */
15761         "columnlockchange" : true
15762     });
15763     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15764 };
15765 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15766     /**
15767      * @cfg {String} header The header text to display in the Grid view.
15768      */
15769     /**
15770      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15771      * {@link Roo.data.Record} definition from which to draw the column's value. If not
15772      * specified, the column's index is used as an index into the Record's data Array.
15773      */
15774     /**
15775      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15776      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15777      */
15778     /**
15779      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15780      * Defaults to the value of the {@link #defaultSortable} property.
15781      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15782      */
15783     /**
15784      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
15785      */
15786     /**
15787      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
15788      */
15789     /**
15790      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15791      */
15792     /**
15793      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15794      */
15795     /**
15796      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15797      * given the cell's data value. See {@link #setRenderer}. If not specified, the
15798      * default renderer uses the raw data value.
15799      */
15800     /**
15801      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
15802      */
15803
15804     /**
15805      * Returns the id of the column at the specified index.
15806      * @param {Number} index The column index
15807      * @return {String} the id
15808      */
15809     getColumnId : function(index){
15810         return this.config[index].id;
15811     },
15812
15813     /**
15814      * Returns the column for a specified id.
15815      * @param {String} id The column id
15816      * @return {Object} the column
15817      */
15818     getColumnById : function(id){
15819         return this.lookup[id];
15820     },
15821
15822     
15823     /**
15824      * Returns the column for a specified dataIndex.
15825      * @param {String} dataIndex The column dataIndex
15826      * @return {Object|Boolean} the column or false if not found
15827      */
15828     getColumnByDataIndex: function(dataIndex){
15829         var index = this.findColumnIndex(dataIndex);
15830         return index > -1 ? this.config[index] : false;
15831     },
15832     
15833     /**
15834      * Returns the index for a specified column id.
15835      * @param {String} id The column id
15836      * @return {Number} the index, or -1 if not found
15837      */
15838     getIndexById : function(id){
15839         for(var i = 0, len = this.config.length; i < len; i++){
15840             if(this.config[i].id == id){
15841                 return i;
15842             }
15843         }
15844         return -1;
15845     },
15846     
15847     /**
15848      * Returns the index for a specified column dataIndex.
15849      * @param {String} dataIndex The column dataIndex
15850      * @return {Number} the index, or -1 if not found
15851      */
15852     
15853     findColumnIndex : function(dataIndex){
15854         for(var i = 0, len = this.config.length; i < len; i++){
15855             if(this.config[i].dataIndex == dataIndex){
15856                 return i;
15857             }
15858         }
15859         return -1;
15860     },
15861     
15862     
15863     moveColumn : function(oldIndex, newIndex){
15864         var c = this.config[oldIndex];
15865         this.config.splice(oldIndex, 1);
15866         this.config.splice(newIndex, 0, c);
15867         this.dataMap = null;
15868         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15869     },
15870
15871     isLocked : function(colIndex){
15872         return this.config[colIndex].locked === true;
15873     },
15874
15875     setLocked : function(colIndex, value, suppressEvent){
15876         if(this.isLocked(colIndex) == value){
15877             return;
15878         }
15879         this.config[colIndex].locked = value;
15880         if(!suppressEvent){
15881             this.fireEvent("columnlockchange", this, colIndex, value);
15882         }
15883     },
15884
15885     getTotalLockedWidth : function(){
15886         var totalWidth = 0;
15887         for(var i = 0; i < this.config.length; i++){
15888             if(this.isLocked(i) && !this.isHidden(i)){
15889                 this.totalWidth += this.getColumnWidth(i);
15890             }
15891         }
15892         return totalWidth;
15893     },
15894
15895     getLockedCount : function(){
15896         for(var i = 0, len = this.config.length; i < len; i++){
15897             if(!this.isLocked(i)){
15898                 return i;
15899             }
15900         }
15901     },
15902
15903     /**
15904      * Returns the number of columns.
15905      * @return {Number}
15906      */
15907     getColumnCount : function(visibleOnly){
15908         if(visibleOnly === true){
15909             var c = 0;
15910             for(var i = 0, len = this.config.length; i < len; i++){
15911                 if(!this.isHidden(i)){
15912                     c++;
15913                 }
15914             }
15915             return c;
15916         }
15917         return this.config.length;
15918     },
15919
15920     /**
15921      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15922      * @param {Function} fn
15923      * @param {Object} scope (optional)
15924      * @return {Array} result
15925      */
15926     getColumnsBy : function(fn, scope){
15927         var r = [];
15928         for(var i = 0, len = this.config.length; i < len; i++){
15929             var c = this.config[i];
15930             if(fn.call(scope||this, c, i) === true){
15931                 r[r.length] = c;
15932             }
15933         }
15934         return r;
15935     },
15936
15937     /**
15938      * Returns true if the specified column is sortable.
15939      * @param {Number} col The column index
15940      * @return {Boolean}
15941      */
15942     isSortable : function(col){
15943         if(typeof this.config[col].sortable == "undefined"){
15944             return this.defaultSortable;
15945         }
15946         return this.config[col].sortable;
15947     },
15948
15949     /**
15950      * Returns the rendering (formatting) function defined for the column.
15951      * @param {Number} col The column index.
15952      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15953      */
15954     getRenderer : function(col){
15955         if(!this.config[col].renderer){
15956             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15957         }
15958         return this.config[col].renderer;
15959     },
15960
15961     /**
15962      * Sets the rendering (formatting) function for a column.
15963      * @param {Number} col The column index
15964      * @param {Function} fn The function to use to process the cell's raw data
15965      * to return HTML markup for the grid view. The render function is called with
15966      * the following parameters:<ul>
15967      * <li>Data value.</li>
15968      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15969      * <li>css A CSS style string to apply to the table cell.</li>
15970      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15971      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15972      * <li>Row index</li>
15973      * <li>Column index</li>
15974      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15975      */
15976     setRenderer : function(col, fn){
15977         this.config[col].renderer = fn;
15978     },
15979
15980     /**
15981      * Returns the width for the specified column.
15982      * @param {Number} col The column index
15983      * @return {Number}
15984      */
15985     getColumnWidth : function(col){
15986         return this.config[col].width * 1 || this.defaultWidth;
15987     },
15988
15989     /**
15990      * Sets the width for a column.
15991      * @param {Number} col The column index
15992      * @param {Number} width The new width
15993      */
15994     setColumnWidth : function(col, width, suppressEvent){
15995         this.config[col].width = width;
15996         this.totalWidth = null;
15997         if(!suppressEvent){
15998              this.fireEvent("widthchange", this, col, width);
15999         }
16000     },
16001
16002     /**
16003      * Returns the total width of all columns.
16004      * @param {Boolean} includeHidden True to include hidden column widths
16005      * @return {Number}
16006      */
16007     getTotalWidth : function(includeHidden){
16008         if(!this.totalWidth){
16009             this.totalWidth = 0;
16010             for(var i = 0, len = this.config.length; i < len; i++){
16011                 if(includeHidden || !this.isHidden(i)){
16012                     this.totalWidth += this.getColumnWidth(i);
16013                 }
16014             }
16015         }
16016         return this.totalWidth;
16017     },
16018
16019     /**
16020      * Returns the header for the specified column.
16021      * @param {Number} col The column index
16022      * @return {String}
16023      */
16024     getColumnHeader : function(col){
16025         return this.config[col].header;
16026     },
16027
16028     /**
16029      * Sets the header for a column.
16030      * @param {Number} col The column index
16031      * @param {String} header The new header
16032      */
16033     setColumnHeader : function(col, header){
16034         this.config[col].header = header;
16035         this.fireEvent("headerchange", this, col, header);
16036     },
16037
16038     /**
16039      * Returns the tooltip for the specified column.
16040      * @param {Number} col The column index
16041      * @return {String}
16042      */
16043     getColumnTooltip : function(col){
16044             return this.config[col].tooltip;
16045     },
16046     /**
16047      * Sets the tooltip for a column.
16048      * @param {Number} col The column index
16049      * @param {String} tooltip The new tooltip
16050      */
16051     setColumnTooltip : function(col, tooltip){
16052             this.config[col].tooltip = tooltip;
16053     },
16054
16055     /**
16056      * Returns the dataIndex for the specified column.
16057      * @param {Number} col The column index
16058      * @return {Number}
16059      */
16060     getDataIndex : function(col){
16061         return this.config[col].dataIndex;
16062     },
16063
16064     /**
16065      * Sets the dataIndex for a column.
16066      * @param {Number} col The column index
16067      * @param {Number} dataIndex The new dataIndex
16068      */
16069     setDataIndex : function(col, dataIndex){
16070         this.config[col].dataIndex = dataIndex;
16071     },
16072
16073     
16074     
16075     /**
16076      * Returns true if the cell is editable.
16077      * @param {Number} colIndex The column index
16078      * @param {Number} rowIndex The row index
16079      * @return {Boolean}
16080      */
16081     isCellEditable : function(colIndex, rowIndex){
16082         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16083     },
16084
16085     /**
16086      * Returns the editor defined for the cell/column.
16087      * return false or null to disable editing.
16088      * @param {Number} colIndex The column index
16089      * @param {Number} rowIndex The row index
16090      * @return {Object}
16091      */
16092     getCellEditor : function(colIndex, rowIndex){
16093         return this.config[colIndex].editor;
16094     },
16095
16096     /**
16097      * Sets if a column is editable.
16098      * @param {Number} col The column index
16099      * @param {Boolean} editable True if the column is editable
16100      */
16101     setEditable : function(col, editable){
16102         this.config[col].editable = editable;
16103     },
16104
16105
16106     /**
16107      * Returns true if the column is hidden.
16108      * @param {Number} colIndex The column index
16109      * @return {Boolean}
16110      */
16111     isHidden : function(colIndex){
16112         return this.config[colIndex].hidden;
16113     },
16114
16115
16116     /**
16117      * Returns true if the column width cannot be changed
16118      */
16119     isFixed : function(colIndex){
16120         return this.config[colIndex].fixed;
16121     },
16122
16123     /**
16124      * Returns true if the column can be resized
16125      * @return {Boolean}
16126      */
16127     isResizable : function(colIndex){
16128         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16129     },
16130     /**
16131      * Sets if a column is hidden.
16132      * @param {Number} colIndex The column index
16133      * @param {Boolean} hidden True if the column is hidden
16134      */
16135     setHidden : function(colIndex, hidden){
16136         this.config[colIndex].hidden = hidden;
16137         this.totalWidth = null;
16138         this.fireEvent("hiddenchange", this, colIndex, hidden);
16139     },
16140
16141     /**
16142      * Sets the editor for a column.
16143      * @param {Number} col The column index
16144      * @param {Object} editor The editor object
16145      */
16146     setEditor : function(col, editor){
16147         this.config[col].editor = editor;
16148     }
16149 });
16150
16151 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16152         if(typeof value == "string" && value.length < 1){
16153             return "&#160;";
16154         }
16155         return value;
16156 };
16157
16158 // Alias for backwards compatibility
16159 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16160
16161 /**
16162  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16163  * @class Roo.bootstrap.Table.RowSelectionModel
16164  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16165  * It supports multiple selections and keyboard selection/navigation. 
16166  * @constructor
16167  * @param {Object} config
16168  */
16169
16170 Roo.bootstrap.Table.RowSelectionModel = function(config){
16171     Roo.apply(this, config);
16172     this.selections = new Roo.util.MixedCollection(false, function(o){
16173         return o.id;
16174     });
16175
16176     this.last = false;
16177     this.lastActive = false;
16178
16179     this.addEvents({
16180         /**
16181              * @event selectionchange
16182              * Fires when the selection changes
16183              * @param {SelectionModel} this
16184              */
16185             "selectionchange" : true,
16186         /**
16187              * @event afterselectionchange
16188              * Fires after the selection changes (eg. by key press or clicking)
16189              * @param {SelectionModel} this
16190              */
16191             "afterselectionchange" : true,
16192         /**
16193              * @event beforerowselect
16194              * Fires when a row is selected being selected, return false to cancel.
16195              * @param {SelectionModel} this
16196              * @param {Number} rowIndex The selected index
16197              * @param {Boolean} keepExisting False if other selections will be cleared
16198              */
16199             "beforerowselect" : true,
16200         /**
16201              * @event rowselect
16202              * Fires when a row is selected.
16203              * @param {SelectionModel} this
16204              * @param {Number} rowIndex The selected index
16205              * @param {Roo.data.Record} r The record
16206              */
16207             "rowselect" : true,
16208         /**
16209              * @event rowdeselect
16210              * Fires when a row is deselected.
16211              * @param {SelectionModel} this
16212              * @param {Number} rowIndex The selected index
16213              */
16214         "rowdeselect" : true
16215     });
16216     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16217     this.locked = false;
16218 };
16219
16220 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16221     /**
16222      * @cfg {Boolean} singleSelect
16223      * True to allow selection of only one row at a time (defaults to false)
16224      */
16225     singleSelect : false,
16226
16227     // private
16228     initEvents : function(){
16229
16230         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16231             this.grid.on("mousedown", this.handleMouseDown, this);
16232         }else{ // allow click to work like normal
16233             this.grid.on("rowclick", this.handleDragableRowClick, this);
16234         }
16235
16236         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16237             "up" : function(e){
16238                 if(!e.shiftKey){
16239                     this.selectPrevious(e.shiftKey);
16240                 }else if(this.last !== false && this.lastActive !== false){
16241                     var last = this.last;
16242                     this.selectRange(this.last,  this.lastActive-1);
16243                     this.grid.getView().focusRow(this.lastActive);
16244                     if(last !== false){
16245                         this.last = last;
16246                     }
16247                 }else{
16248                     this.selectFirstRow();
16249                 }
16250                 this.fireEvent("afterselectionchange", this);
16251             },
16252             "down" : function(e){
16253                 if(!e.shiftKey){
16254                     this.selectNext(e.shiftKey);
16255                 }else if(this.last !== false && this.lastActive !== false){
16256                     var last = this.last;
16257                     this.selectRange(this.last,  this.lastActive+1);
16258                     this.grid.getView().focusRow(this.lastActive);
16259                     if(last !== false){
16260                         this.last = last;
16261                     }
16262                 }else{
16263                     this.selectFirstRow();
16264                 }
16265                 this.fireEvent("afterselectionchange", this);
16266             },
16267             scope: this
16268         });
16269
16270         var view = this.grid.view;
16271         view.on("refresh", this.onRefresh, this);
16272         view.on("rowupdated", this.onRowUpdated, this);
16273         view.on("rowremoved", this.onRemove, this);
16274     },
16275
16276     // private
16277     onRefresh : function(){
16278         var ds = this.grid.dataSource, i, v = this.grid.view;
16279         var s = this.selections;
16280         s.each(function(r){
16281             if((i = ds.indexOfId(r.id)) != -1){
16282                 v.onRowSelect(i);
16283             }else{
16284                 s.remove(r);
16285             }
16286         });
16287     },
16288
16289     // private
16290     onRemove : function(v, index, r){
16291         this.selections.remove(r);
16292     },
16293
16294     // private
16295     onRowUpdated : function(v, index, r){
16296         if(this.isSelected(r)){
16297             v.onRowSelect(index);
16298         }
16299     },
16300
16301     /**
16302      * Select records.
16303      * @param {Array} records The records to select
16304      * @param {Boolean} keepExisting (optional) True to keep existing selections
16305      */
16306     selectRecords : function(records, keepExisting){
16307         if(!keepExisting){
16308             this.clearSelections();
16309         }
16310         var ds = this.grid.dataSource;
16311         for(var i = 0, len = records.length; i < len; i++){
16312             this.selectRow(ds.indexOf(records[i]), true);
16313         }
16314     },
16315
16316     /**
16317      * Gets the number of selected rows.
16318      * @return {Number}
16319      */
16320     getCount : function(){
16321         return this.selections.length;
16322     },
16323
16324     /**
16325      * Selects the first row in the grid.
16326      */
16327     selectFirstRow : function(){
16328         this.selectRow(0);
16329     },
16330
16331     /**
16332      * Select the last row.
16333      * @param {Boolean} keepExisting (optional) True to keep existing selections
16334      */
16335     selectLastRow : function(keepExisting){
16336         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16337     },
16338
16339     /**
16340      * Selects the row immediately following the last selected row.
16341      * @param {Boolean} keepExisting (optional) True to keep existing selections
16342      */
16343     selectNext : function(keepExisting){
16344         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16345             this.selectRow(this.last+1, keepExisting);
16346             this.grid.getView().focusRow(this.last);
16347         }
16348     },
16349
16350     /**
16351      * Selects the row that precedes the last selected row.
16352      * @param {Boolean} keepExisting (optional) True to keep existing selections
16353      */
16354     selectPrevious : function(keepExisting){
16355         if(this.last){
16356             this.selectRow(this.last-1, keepExisting);
16357             this.grid.getView().focusRow(this.last);
16358         }
16359     },
16360
16361     /**
16362      * Returns the selected records
16363      * @return {Array} Array of selected records
16364      */
16365     getSelections : function(){
16366         return [].concat(this.selections.items);
16367     },
16368
16369     /**
16370      * Returns the first selected record.
16371      * @return {Record}
16372      */
16373     getSelected : function(){
16374         return this.selections.itemAt(0);
16375     },
16376
16377
16378     /**
16379      * Clears all selections.
16380      */
16381     clearSelections : function(fast){
16382         if(this.locked) return;
16383         if(fast !== true){
16384             var ds = this.grid.dataSource;
16385             var s = this.selections;
16386             s.each(function(r){
16387                 this.deselectRow(ds.indexOfId(r.id));
16388             }, this);
16389             s.clear();
16390         }else{
16391             this.selections.clear();
16392         }
16393         this.last = false;
16394     },
16395
16396
16397     /**
16398      * Selects all rows.
16399      */
16400     selectAll : function(){
16401         if(this.locked) return;
16402         this.selections.clear();
16403         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16404             this.selectRow(i, true);
16405         }
16406     },
16407
16408     /**
16409      * Returns True if there is a selection.
16410      * @return {Boolean}
16411      */
16412     hasSelection : function(){
16413         return this.selections.length > 0;
16414     },
16415
16416     /**
16417      * Returns True if the specified row is selected.
16418      * @param {Number/Record} record The record or index of the record to check
16419      * @return {Boolean}
16420      */
16421     isSelected : function(index){
16422         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16423         return (r && this.selections.key(r.id) ? true : false);
16424     },
16425
16426     /**
16427      * Returns True if the specified record id is selected.
16428      * @param {String} id The id of record to check
16429      * @return {Boolean}
16430      */
16431     isIdSelected : function(id){
16432         return (this.selections.key(id) ? true : false);
16433     },
16434
16435     // private
16436     handleMouseDown : function(e, t){
16437         var view = this.grid.getView(), rowIndex;
16438         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16439             return;
16440         };
16441         if(e.shiftKey && this.last !== false){
16442             var last = this.last;
16443             this.selectRange(last, rowIndex, e.ctrlKey);
16444             this.last = last; // reset the last
16445             view.focusRow(rowIndex);
16446         }else{
16447             var isSelected = this.isSelected(rowIndex);
16448             if(e.button !== 0 && isSelected){
16449                 view.focusRow(rowIndex);
16450             }else if(e.ctrlKey && isSelected){
16451                 this.deselectRow(rowIndex);
16452             }else if(!isSelected){
16453                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16454                 view.focusRow(rowIndex);
16455             }
16456         }
16457         this.fireEvent("afterselectionchange", this);
16458     },
16459     // private
16460     handleDragableRowClick :  function(grid, rowIndex, e) 
16461     {
16462         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16463             this.selectRow(rowIndex, false);
16464             grid.view.focusRow(rowIndex);
16465              this.fireEvent("afterselectionchange", this);
16466         }
16467     },
16468     
16469     /**
16470      * Selects multiple rows.
16471      * @param {Array} rows Array of the indexes of the row to select
16472      * @param {Boolean} keepExisting (optional) True to keep existing selections
16473      */
16474     selectRows : function(rows, keepExisting){
16475         if(!keepExisting){
16476             this.clearSelections();
16477         }
16478         for(var i = 0, len = rows.length; i < len; i++){
16479             this.selectRow(rows[i], true);
16480         }
16481     },
16482
16483     /**
16484      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16485      * @param {Number} startRow The index of the first row in the range
16486      * @param {Number} endRow The index of the last row in the range
16487      * @param {Boolean} keepExisting (optional) True to retain existing selections
16488      */
16489     selectRange : function(startRow, endRow, keepExisting){
16490         if(this.locked) return;
16491         if(!keepExisting){
16492             this.clearSelections();
16493         }
16494         if(startRow <= endRow){
16495             for(var i = startRow; i <= endRow; i++){
16496                 this.selectRow(i, true);
16497             }
16498         }else{
16499             for(var i = startRow; i >= endRow; i--){
16500                 this.selectRow(i, true);
16501             }
16502         }
16503     },
16504
16505     /**
16506      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16507      * @param {Number} startRow The index of the first row in the range
16508      * @param {Number} endRow The index of the last row in the range
16509      */
16510     deselectRange : function(startRow, endRow, preventViewNotify){
16511         if(this.locked) return;
16512         for(var i = startRow; i <= endRow; i++){
16513             this.deselectRow(i, preventViewNotify);
16514         }
16515     },
16516
16517     /**
16518      * Selects a row.
16519      * @param {Number} row The index of the row to select
16520      * @param {Boolean} keepExisting (optional) True to keep existing selections
16521      */
16522     selectRow : function(index, keepExisting, preventViewNotify){
16523         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16524         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16525             if(!keepExisting || this.singleSelect){
16526                 this.clearSelections();
16527             }
16528             var r = this.grid.dataSource.getAt(index);
16529             this.selections.add(r);
16530             this.last = this.lastActive = index;
16531             if(!preventViewNotify){
16532                 this.grid.getView().onRowSelect(index);
16533             }
16534             this.fireEvent("rowselect", this, index, r);
16535             this.fireEvent("selectionchange", this);
16536         }
16537     },
16538
16539     /**
16540      * Deselects a row.
16541      * @param {Number} row The index of the row to deselect
16542      */
16543     deselectRow : function(index, preventViewNotify){
16544         if(this.locked) return;
16545         if(this.last == index){
16546             this.last = false;
16547         }
16548         if(this.lastActive == index){
16549             this.lastActive = false;
16550         }
16551         var r = this.grid.dataSource.getAt(index);
16552         this.selections.remove(r);
16553         if(!preventViewNotify){
16554             this.grid.getView().onRowDeselect(index);
16555         }
16556         this.fireEvent("rowdeselect", this, index);
16557         this.fireEvent("selectionchange", this);
16558     },
16559
16560     // private
16561     restoreLast : function(){
16562         if(this._last){
16563             this.last = this._last;
16564         }
16565     },
16566
16567     // private
16568     acceptsNav : function(row, col, cm){
16569         return !cm.isHidden(col) && cm.isCellEditable(col, row);
16570     },
16571
16572     // private
16573     onEditorKey : function(field, e){
16574         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16575         if(k == e.TAB){
16576             e.stopEvent();
16577             ed.completeEdit();
16578             if(e.shiftKey){
16579                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16580             }else{
16581                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16582             }
16583         }else if(k == e.ENTER && !e.ctrlKey){
16584             e.stopEvent();
16585             ed.completeEdit();
16586             if(e.shiftKey){
16587                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16588             }else{
16589                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16590             }
16591         }else if(k == e.ESC){
16592             ed.cancelEdit();
16593         }
16594         if(newCell){
16595             g.startEditing(newCell[0], newCell[1]);
16596         }
16597     }
16598 });/*
16599  * - LGPL
16600  *
16601  * element
16602  * 
16603  */
16604
16605 /**
16606  * @class Roo.bootstrap.MessageBar
16607  * @extends Roo.bootstrap.Component
16608  * Bootstrap MessageBar class
16609  * @cfg {String} html contents of the MessageBar
16610  * @cfg {String} weight (info | success | warning | danger) default info
16611  * @cfg {String} beforeClass insert the bar before the given class
16612  * @cfg {Boolean} closable (true | false) default false
16613  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16614  * 
16615  * @constructor
16616  * Create a new Element
16617  * @param {Object} config The config object
16618  */
16619
16620 Roo.bootstrap.MessageBar = function(config){
16621     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16622 };
16623
16624 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
16625     
16626     html: '',
16627     weight: 'info',
16628     closable: false,
16629     fixed: false,
16630     beforeClass: 'bootstrap-sticky-wrap',
16631     
16632     getAutoCreate : function(){
16633         
16634         var cfg = {
16635             tag: 'div',
16636             cls: 'alert alert-dismissable alert-' + this.weight,
16637             cn: [
16638                 {
16639                     tag: 'span',
16640                     cls: 'message',
16641                     html: this.html || ''
16642                 }
16643             ]
16644         }
16645         
16646         if(this.fixed){
16647             cfg.cls += ' alert-messages-fixed';
16648         }
16649         
16650         if(this.closable){
16651             cfg.cn.push({
16652                 tag: 'button',
16653                 cls: 'close',
16654                 html: 'x'
16655             });
16656         }
16657         
16658         return cfg;
16659     },
16660     
16661     onRender : function(ct, position)
16662     {
16663         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16664         
16665         if(!this.el){
16666             var cfg = Roo.apply({},  this.getAutoCreate());
16667             cfg.id = Roo.id();
16668             
16669             if (this.cls) {
16670                 cfg.cls += ' ' + this.cls;
16671             }
16672             if (this.style) {
16673                 cfg.style = this.style;
16674             }
16675             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16676             
16677             this.el.setVisibilityMode(Roo.Element.DISPLAY);
16678         }
16679         
16680         this.el.select('>button.close').on('click', this.hide, this);
16681         
16682     },
16683     
16684     show : function()
16685     {
16686         if (!this.rendered) {
16687             this.render();
16688         }
16689         
16690         this.el.show();
16691         
16692         this.fireEvent('show', this);
16693         
16694     },
16695     
16696     hide : function()
16697     {
16698         if (!this.rendered) {
16699             this.render();
16700         }
16701         
16702         this.el.hide();
16703         
16704         this.fireEvent('hide', this);
16705     },
16706     
16707     update : function()
16708     {
16709 //        var e = this.el.dom.firstChild;
16710 //        
16711 //        if(this.closable){
16712 //            e = e.nextSibling;
16713 //        }
16714 //        
16715 //        e.data = this.html || '';
16716
16717         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
16718     }
16719    
16720 });
16721
16722  
16723
16724