Roo/bootstrap/TabPanel.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298     if (this.cls && this.cls.length) {
299         Roo.get(document.body).addClass(this.cls);
300     }
301 };
302
303 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
304       
305         autoCreate : {
306         cls: 'container'
307     },
308     onRender : function(ct, position)
309     {
310        /* Roo.log("Roo.bootstrap.Body - onRender");
311         if (this.cls && this.cls.length) {
312             Roo.get(document.body).addClass(this.cls);
313         }
314         // style??? xttr???
315         */
316     }
317     
318     
319  
320    
321 });
322
323  /*
324  * - LGPL
325  *
326  * button group
327  * 
328  */
329
330
331 /**
332  * @class Roo.bootstrap.ButtonGroup
333  * @extends Roo.bootstrap.Component
334  * Bootstrap ButtonGroup class
335  * @cfg {String} size lg | sm | xs (default empty normal)
336  * @cfg {String} align vertical | justified  (default none)
337  * @cfg {String} direction up | down (default down)
338  * @cfg {Boolean} toolbar false | true
339  * @cfg {Boolean} btn true | false
340  * 
341  * 
342  * @constructor
343  * Create a new Input
344  * @param {Object} config The config object
345  */
346
347 Roo.bootstrap.ButtonGroup = function(config){
348     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
349 };
350
351 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
352     
353     size: '',
354     align: '',
355     direction: '',
356     toolbar: false,
357     btn: true,
358
359     getAutoCreate : function(){
360         var cfg = {
361             cls: 'btn-group',
362             html : null
363         }
364         
365         cfg.html = this.html || cfg.html;
366         
367         if (this.toolbar) {
368             cfg = {
369                 cls: 'btn-toolbar',
370                 html: null
371             }
372             
373             return cfg;
374         }
375         
376         if (['vertical','justified'].indexOf(this.align)!==-1) {
377             cfg.cls = 'btn-group-' + this.align;
378             
379             if (this.align == 'justified') {
380                 console.log(this.items);
381             }
382         }
383         
384         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
385             cfg.cls += ' btn-group-' + this.size;
386         }
387         
388         if (this.direction == 'up') {
389             cfg.cls += ' dropup' ;
390         }
391         
392         return cfg;
393     }
394    
395 });
396
397  /*
398  * - LGPL
399  *
400  * button
401  * 
402  */
403
404 /**
405  * @class Roo.bootstrap.Button
406  * @extends Roo.bootstrap.Component
407  * Bootstrap Button class
408  * @cfg {String} html The button content
409  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
410  * @cfg {String} size empty | lg | sm | xs
411  * @cfg {String} tag empty | a | input | submit
412  * @cfg {String} href empty or href
413  * @cfg {Boolean} disabled false | true
414  * @cfg {Boolean} isClose false | true
415  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
416  * @cfg {String} badge text for badge
417  * @cfg {String} theme default (or empty) | glow
418  * @cfg {Boolean} inverse false | true
419  * @cfg {Boolean} toggle false | true
420  * @cfg {String} ontext text for on toggle state
421  * @cfg {String} offtext text for off toggle state
422  * @cfg {Boolean} defaulton true | false
423  * @cfg {Boolean} preventDefault (true | false) default true
424  * @cfg {Boolean} removeClass true | false remove the standard class..
425  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
426  * 
427  * @constructor
428  * Create a new button
429  * @param {Object} config The config object
430  */
431
432
433 Roo.bootstrap.Button = function(config){
434     Roo.bootstrap.Button.superclass.constructor.call(this, config);
435     this.addEvents({
436         // raw events
437         /**
438          * @event click
439          * When a butotn is pressed
440          * @param {Roo.EventObject} e
441          */
442         "click" : true,
443          /**
444          * @event toggle
445          * After the button has been toggles
446          * @param {Roo.EventObject} e
447          * @param {boolean} pressed (also available as button.pressed)
448          */
449         "toggle" : true
450     });
451 };
452
453 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
454     html: false,
455     active: false,
456     weight: '',
457     size: '',
458     tag: 'button',
459     href: '',
460     disabled: false,
461     isClose: false,
462     glyphicon: '',
463     badge: '',
464     theme: 'default',
465     inverse: false,
466     
467     toggle: false,
468     ontext: 'ON',
469     offtext: 'OFF',
470     defaulton: true,
471     preventDefault: true,
472     removeClass: false,
473     name: false,
474     target: false,
475     
476     
477     pressed : null,
478      
479     
480     getAutoCreate : function(){
481         
482         var cfg = {
483             tag : 'button',
484             cls : 'roo-button',
485             html: ''
486         };
487         
488         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
489             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
490             this.tag = 'button';
491         } else {
492             cfg.tag = this.tag;
493         }
494         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
495         
496         if (this.toggle == true) {
497             cfg={
498                 tag: 'div',
499                 cls: 'slider-frame roo-button',
500                 cn: [
501                     {
502                         tag: 'span',
503                         'data-on-text':'ON',
504                         'data-off-text':'OFF',
505                         cls: 'slider-button',
506                         html: this.offtext
507                     }
508                 ]
509             };
510             
511             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
512                 cfg.cls += ' '+this.weight;
513             }
514             
515             return cfg;
516         }
517         
518         if (this.isClose) {
519             cfg.cls += ' close';
520             
521             cfg["aria-hidden"] = true;
522             
523             cfg.html = "&times;";
524             
525             return cfg;
526         }
527         
528          
529         if (this.theme==='default') {
530             cfg.cls = 'btn roo-button';
531             
532             //if (this.parentType != 'Navbar') {
533             this.weight = this.weight.length ?  this.weight : 'default';
534             //}
535             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
536                 
537                 cfg.cls += ' btn-' + this.weight;
538             }
539         } else if (this.theme==='glow') {
540             
541             cfg.tag = 'a';
542             cfg.cls = 'btn-glow roo-button';
543             
544             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
545                 
546                 cfg.cls += ' ' + this.weight;
547             }
548         }
549    
550         
551         if (this.inverse) {
552             this.cls += ' inverse';
553         }
554         
555         
556         if (this.active) {
557             cfg.cls += ' active';
558         }
559         
560         if (this.disabled) {
561             cfg.disabled = 'disabled';
562         }
563         
564         if (this.items) {
565             Roo.log('changing to ul' );
566             cfg.tag = 'ul';
567             this.glyphicon = 'caret';
568         }
569         
570         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
571          
572         //gsRoo.log(this.parentType);
573         if (this.parentType === 'Navbar' && !this.parent().bar) {
574             Roo.log('changing to li?');
575             
576             cfg.tag = 'li';
577             
578             cfg.cls = '';
579             cfg.cn =  [{
580                 tag : 'a',
581                 cls : 'roo-button',
582                 html : this.html,
583                 href : this.href || '#'
584             }];
585             if (this.menu) {
586                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
587                 cfg.cls += ' dropdown';
588             }   
589             
590             delete cfg.html;
591             
592         }
593         
594        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
595         
596         if (this.glyphicon) {
597             cfg.html = ' ' + cfg.html;
598             
599             cfg.cn = [
600                 {
601                     tag: 'span',
602                     cls: 'glyphicon glyphicon-' + this.glyphicon
603                 }
604             ];
605         }
606         
607         if (this.badge) {
608             cfg.html += ' ';
609             
610             cfg.tag = 'a';
611             
612 //            cfg.cls='btn roo-button';
613             
614             cfg.href=this.href;
615             
616             var value = cfg.html;
617             
618             if(this.glyphicon){
619                 value = {
620                             tag: 'span',
621                             cls: 'glyphicon glyphicon-' + this.glyphicon,
622                             html: this.html
623                         };
624                 
625             }
626             
627             cfg.cn = [
628                 value,
629                 {
630                     tag: 'span',
631                     cls: 'badge',
632                     html: this.badge
633                 }
634             ];
635             
636             cfg.html='';
637         }
638         
639         if (this.menu) {
640             cfg.cls += ' dropdown';
641             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
642         }
643         
644         if (cfg.tag !== 'a' && this.href !== '') {
645             throw "Tag must be a to set href.";
646         } else if (this.href.length > 0) {
647             cfg.href = this.href;
648         }
649         
650         if(this.removeClass){
651             cfg.cls = '';
652         }
653         
654         if(this.target){
655             cfg.target = this.target;
656         }
657         
658         return cfg;
659     },
660     initEvents: function() {
661        // Roo.log('init events?');
662 //        Roo.log(this.el.dom);
663        if (this.el.hasClass('roo-button')) {
664             this.el.on('click', this.onClick, this);
665        } else {
666             this.el.select('.roo-button').on('click', this.onClick, this);
667        }
668        
669        if(this.removeClass){
670            this.el.on('click', this.onClick, this);
671        }
672        
673        this.el.enableDisplayMode();
674         
675     },
676     onClick : function(e)
677     {
678         if (this.disabled) {
679             return;
680         }
681         
682         Roo.log('button on click ');
683         if(this.preventDefault){
684             e.preventDefault();
685         }
686         if (this.pressed === true || this.pressed === false) {
687             this.pressed = !this.pressed;
688             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
689             this.fireEvent('toggle', this, e, this.pressed);
690         }
691         
692         
693         this.fireEvent('click', this, e);
694     },
695     
696     /**
697      * Enables this button
698      */
699     enable : function()
700     {
701         this.disabled = false;
702         this.el.removeClass('disabled');
703     },
704     
705     /**
706      * Disable this button
707      */
708     disable : function()
709     {
710         this.disabled = true;
711         this.el.addClass('disabled');
712     },
713      /**
714      * sets the active state on/off, 
715      * @param {Boolean} state (optional) Force a particular state
716      */
717     setActive : function(v) {
718         
719         this.el[v ? 'addClass' : 'removeClass']('active');
720     },
721      /**
722      * toggles the current active state 
723      */
724     toggleActive : function()
725     {
726        var active = this.el.hasClass('active');
727        this.setActive(!active);
728        
729         
730     },
731     setText : function(str)
732     {
733         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
734     },
735     hide: function() {
736        
737      
738         this.el.hide();   
739     },
740     show: function() {
741        
742         this.el.show();   
743     }
744     
745     
746 });
747
748  /*
749  * - LGPL
750  *
751  * column
752  * 
753  */
754
755 /**
756  * @class Roo.bootstrap.Column
757  * @extends Roo.bootstrap.Component
758  * Bootstrap Column class
759  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
760  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
761  * @cfg {Number} md colspan out of 12 for computer-sized screens
762  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
763  * @cfg {String} html content of column.
764  * 
765  * @constructor
766  * Create a new Column
767  * @param {Object} config The config object
768  */
769
770 Roo.bootstrap.Column = function(config){
771     Roo.bootstrap.Column.superclass.constructor.call(this, config);
772 };
773
774 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
775     
776     xs: null,
777     sm: null,
778     md: null,
779     lg: null,
780     html: '',
781     offset: 0,
782     
783     getAutoCreate : function(){
784         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
785         
786         cfg = {
787             tag: 'div',
788             cls: 'column'
789         };
790         
791         var settings=this;
792         ['xs','sm','md','lg'].map(function(size){
793             if (settings[size]) {
794                 cfg.cls += ' col-' + size + '-' + settings[size];
795             }
796         });
797         if (this.html.length) {
798             cfg.html = this.html;
799         }
800         
801         return cfg;
802     }
803    
804 });
805
806  
807
808  /*
809  * - LGPL
810  *
811  * page container.
812  * 
813  */
814
815
816 /**
817  * @class Roo.bootstrap.Container
818  * @extends Roo.bootstrap.Component
819  * Bootstrap Container class
820  * @cfg {Boolean} jumbotron is it a jumbotron element
821  * @cfg {String} html content of element
822  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
823  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
824  * @cfg {String} header content of header (for panel)
825  * @cfg {String} footer content of footer (for panel)
826  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
827  * @cfg {String} tag (header|aside|section) type of HTML tag.
828
829  *     
830  * @constructor
831  * Create a new Container
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Container = function(config){
836     Roo.bootstrap.Container.superclass.constructor.call(this, config);
837 };
838
839 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
840     
841     jumbotron : false,
842     well: '',
843     panel : '',
844     header: '',
845     footer : '',
846     sticky: '',
847     tag : false,
848   
849      
850     getChildContainer : function() {
851         
852         if(!this.el){
853             return false;
854         }
855         
856         if (this.panel.length) {
857             return this.el.select('.panel-body',true).first();
858         }
859         
860         return this.el;
861     },
862     
863     
864     getAutoCreate : function(){
865         
866         var cfg = {
867             tag : this.tag || 'div',
868             html : '',
869             cls : ''
870         };
871         if (this.jumbotron) {
872             cfg.cls = 'jumbotron';
873         }
874         // - this is applied by the parent..
875         //if (this.cls) {
876         //    cfg.cls = this.cls + '';
877         //}
878         
879         if (this.sticky.length) {
880             
881             var bd = Roo.get(document.body);
882             if (!bd.hasClass('bootstrap-sticky')) {
883                 bd.addClass('bootstrap-sticky');
884                 Roo.select('html',true).setStyle('height', '100%');
885             }
886              
887             cfg.cls += 'bootstrap-sticky-' + this.sticky;
888         }
889         
890         
891         if (this.well.length) {
892             switch (this.well) {
893                 case 'lg':
894                 case 'sm':
895                     cfg.cls +=' well well-' +this.well;
896                     break;
897                 default:
898                     cfg.cls +=' well';
899                     break;
900             }
901         }
902         
903         var body = cfg;
904         
905         if (this.panel.length) {
906             cfg.cls += ' panel panel-' + this.panel;
907             cfg.cn = [];
908             if (this.header.length) {
909                 cfg.cn.push({
910                     
911                     cls : 'panel-heading',
912                     cn : [{
913                         tag: 'h3',
914                         cls : 'panel-title',
915                         html : this.header
916                     }]
917                     
918                 });
919             }
920             body = false;
921             cfg.cn.push({
922                 cls : 'panel-body',
923                 html : this.html
924             });
925             
926             
927             if (this.footer.length) {
928                 cfg.cn.push({
929                     cls : 'panel-footer',
930                     html : this.footer
931                     
932                 });
933             }
934             
935         }
936         
937         if (body) {
938             body.html = this.html || cfg.html;
939         }
940         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
941             cfg.cls =  'container';
942         }
943         
944         return cfg;
945     }
946    
947 });
948
949  /*
950  * - LGPL
951  *
952  * image
953  * 
954  */
955
956
957 /**
958  * @class Roo.bootstrap.Img
959  * @extends Roo.bootstrap.Component
960  * Bootstrap Img class
961  * @cfg {Boolean} imgResponsive false | true
962  * @cfg {String} border rounded | circle | thumbnail
963  * @cfg {String} src image source
964  * @cfg {String} alt image alternative text
965  * @cfg {String} href a tag href
966  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
967  * 
968  * @constructor
969  * Create a new Input
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Img = function(config){
974     Roo.bootstrap.Img.superclass.constructor.call(this, config);
975     
976     this.addEvents({
977         // img events
978         /**
979          * @event click
980          * The img click event for the img.
981          * @param {Roo.EventObject} e
982          */
983         "click" : true
984     });
985 };
986
987 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
988     
989     imgResponsive: true,
990     border: '',
991     src: '',
992     href: false,
993     target: false,
994
995     getAutoCreate : function(){
996         
997         var cfg = {
998             tag: 'img',
999             cls: (this.imgResponsive) ? 'img-responsive' : '',
1000             html : null
1001         }
1002         
1003         cfg.html = this.html || cfg.html;
1004         
1005         cfg.src = this.src || cfg.src;
1006         
1007         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1008             cfg.cls += ' img-' + this.border;
1009         }
1010         
1011         if(this.alt){
1012             cfg.alt = this.alt;
1013         }
1014         
1015         if(this.href){
1016             var a = {
1017                 tag: 'a',
1018                 href: this.href,
1019                 cn: [
1020                     cfg
1021                 ]
1022             }
1023             
1024             if(this.target){
1025                 a.target = this.target;
1026             }
1027             
1028         }
1029         
1030         
1031         return (this.href) ? a : cfg;
1032     },
1033     
1034     initEvents: function() {
1035         
1036         if(!this.href){
1037             this.el.on('click', this.onClick, this);
1038         }
1039     },
1040     
1041     onClick : function(e)
1042     {
1043         Roo.log('img onclick');
1044         this.fireEvent('click', this, e);
1045     }
1046    
1047 });
1048
1049  /*
1050  * - LGPL
1051  *
1052  * image
1053  * 
1054  */
1055
1056
1057 /**
1058  * @class Roo.bootstrap.Link
1059  * @extends Roo.bootstrap.Component
1060  * Bootstrap Link Class
1061  * @cfg {String} alt image alternative text
1062  * @cfg {String} href a tag href
1063  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1064  * @cfg {String} html the content of the link.
1065
1066  * 
1067  * @constructor
1068  * Create a new Input
1069  * @param {Object} config The config object
1070  */
1071
1072 Roo.bootstrap.Link = function(config){
1073     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1074     
1075     this.addEvents({
1076         // img events
1077         /**
1078          * @event click
1079          * The img click event for the img.
1080          * @param {Roo.EventObject} e
1081          */
1082         "click" : true
1083     });
1084 };
1085
1086 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1087     
1088     href: false,
1089     target: false,
1090
1091     getAutoCreate : function(){
1092         
1093         var cfg = {
1094             tag: 'a',
1095             html : this.html || 'html-missing'
1096         }
1097         
1098         
1099         if(this.alt){
1100             cfg.alt = this.alt;
1101         }
1102         cfg.href = this.href || '#';
1103         if(this.target){
1104             cfg.target = this.target;
1105         }
1106         
1107         return cfg;
1108     },
1109     
1110     initEvents: function() {
1111         
1112         if(!this.href){
1113             this.el.on('click', this.onClick, this);
1114         }
1115     },
1116     
1117     onClick : function(e)
1118     {
1119         //Roo.log('img onclick');
1120         this.fireEvent('click', this, e);
1121     }
1122    
1123 });
1124
1125  /*
1126  * - LGPL
1127  *
1128  * header
1129  * 
1130  */
1131
1132 /**
1133  * @class Roo.bootstrap.Header
1134  * @extends Roo.bootstrap.Component
1135  * Bootstrap Header class
1136  * @cfg {String} html content of header
1137  * @cfg {Number} level (1|2|3|4|5|6) default 1
1138  * 
1139  * @constructor
1140  * Create a new Header
1141  * @param {Object} config The config object
1142  */
1143
1144
1145 Roo.bootstrap.Header  = function(config){
1146     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1147 };
1148
1149 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1150     
1151     //href : false,
1152     html : false,
1153     level : 1,
1154     
1155     
1156     
1157     getAutoCreate : function(){
1158         
1159         var cfg = {
1160             tag: 'h' + (1 *this.level),
1161             html: this.html || 'fill in html'
1162         } ;
1163         
1164         return cfg;
1165     }
1166    
1167 });
1168
1169  
1170
1171  /*
1172  * Based on:
1173  * Ext JS Library 1.1.1
1174  * Copyright(c) 2006-2007, Ext JS, LLC.
1175  *
1176  * Originally Released Under LGPL - original licence link has changed is not relivant.
1177  *
1178  * Fork - LGPL
1179  * <script type="text/javascript">
1180  */
1181  
1182 /**
1183  * @class Roo.bootstrap.MenuMgr
1184  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1185  * @singleton
1186  */
1187 Roo.bootstrap.MenuMgr = function(){
1188    var menus, active, groups = {}, attached = false, lastShow = new Date();
1189
1190    // private - called when first menu is created
1191    function init(){
1192        menus = {};
1193        active = new Roo.util.MixedCollection();
1194        Roo.get(document).addKeyListener(27, function(){
1195            if(active.length > 0){
1196                hideAll();
1197            }
1198        });
1199    }
1200
1201    // private
1202    function hideAll(){
1203        if(active && active.length > 0){
1204            var c = active.clone();
1205            c.each(function(m){
1206                m.hide();
1207            });
1208        }
1209    }
1210
1211    // private
1212    function onHide(m){
1213        active.remove(m);
1214        if(active.length < 1){
1215            Roo.get(document).un("mouseup", onMouseDown);
1216             
1217            attached = false;
1218        }
1219    }
1220
1221    // private
1222    function onShow(m){
1223        var last = active.last();
1224        lastShow = new Date();
1225        active.add(m);
1226        if(!attached){
1227           Roo.get(document).on("mouseup", onMouseDown);
1228            
1229            attached = true;
1230        }
1231        if(m.parentMenu){
1232           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1233           m.parentMenu.activeChild = m;
1234        }else if(last && last.isVisible()){
1235           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1236        }
1237    }
1238
1239    // private
1240    function onBeforeHide(m){
1241        if(m.activeChild){
1242            m.activeChild.hide();
1243        }
1244        if(m.autoHideTimer){
1245            clearTimeout(m.autoHideTimer);
1246            delete m.autoHideTimer;
1247        }
1248    }
1249
1250    // private
1251    function onBeforeShow(m){
1252        var pm = m.parentMenu;
1253        if(!pm && !m.allowOtherMenus){
1254            hideAll();
1255        }else if(pm && pm.activeChild && active != m){
1256            pm.activeChild.hide();
1257        }
1258    }
1259
1260    // private
1261    function onMouseDown(e){
1262         Roo.log("on MouseDown");
1263         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1264            hideAll();
1265         }
1266         
1267         
1268    }
1269
1270    // private
1271    function onBeforeCheck(mi, state){
1272        if(state){
1273            var g = groups[mi.group];
1274            for(var i = 0, l = g.length; i < l; i++){
1275                if(g[i] != mi){
1276                    g[i].setChecked(false);
1277                }
1278            }
1279        }
1280    }
1281
1282    return {
1283
1284        /**
1285         * Hides all menus that are currently visible
1286         */
1287        hideAll : function(){
1288             hideAll();  
1289        },
1290
1291        // private
1292        register : function(menu){
1293            if(!menus){
1294                init();
1295            }
1296            menus[menu.id] = menu;
1297            menu.on("beforehide", onBeforeHide);
1298            menu.on("hide", onHide);
1299            menu.on("beforeshow", onBeforeShow);
1300            menu.on("show", onShow);
1301            var g = menu.group;
1302            if(g && menu.events["checkchange"]){
1303                if(!groups[g]){
1304                    groups[g] = [];
1305                }
1306                groups[g].push(menu);
1307                menu.on("checkchange", onCheck);
1308            }
1309        },
1310
1311         /**
1312          * Returns a {@link Roo.menu.Menu} object
1313          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1314          * be used to generate and return a new Menu instance.
1315          */
1316        get : function(menu){
1317            if(typeof menu == "string"){ // menu id
1318                return menus[menu];
1319            }else if(menu.events){  // menu instance
1320                return menu;
1321            }
1322            /*else if(typeof menu.length == 'number'){ // array of menu items?
1323                return new Roo.bootstrap.Menu({items:menu});
1324            }else{ // otherwise, must be a config
1325                return new Roo.bootstrap.Menu(menu);
1326            }
1327            */
1328            return false;
1329        },
1330
1331        // private
1332        unregister : function(menu){
1333            delete menus[menu.id];
1334            menu.un("beforehide", onBeforeHide);
1335            menu.un("hide", onHide);
1336            menu.un("beforeshow", onBeforeShow);
1337            menu.un("show", onShow);
1338            var g = menu.group;
1339            if(g && menu.events["checkchange"]){
1340                groups[g].remove(menu);
1341                menu.un("checkchange", onCheck);
1342            }
1343        },
1344
1345        // private
1346        registerCheckable : function(menuItem){
1347            var g = menuItem.group;
1348            if(g){
1349                if(!groups[g]){
1350                    groups[g] = [];
1351                }
1352                groups[g].push(menuItem);
1353                menuItem.on("beforecheckchange", onBeforeCheck);
1354            }
1355        },
1356
1357        // private
1358        unregisterCheckable : function(menuItem){
1359            var g = menuItem.group;
1360            if(g){
1361                groups[g].remove(menuItem);
1362                menuItem.un("beforecheckchange", onBeforeCheck);
1363            }
1364        }
1365    };
1366 }();/*
1367  * - LGPL
1368  *
1369  * menu
1370  * 
1371  */
1372
1373 /**
1374  * @class Roo.bootstrap.Menu
1375  * @extends Roo.bootstrap.Component
1376  * Bootstrap Menu class - container for MenuItems
1377  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1378  * 
1379  * @constructor
1380  * Create a new Menu
1381  * @param {Object} config The config object
1382  */
1383
1384
1385 Roo.bootstrap.Menu = function(config){
1386     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1387     if (this.registerMenu) {
1388         Roo.bootstrap.MenuMgr.register(this);
1389     }
1390     this.addEvents({
1391         /**
1392          * @event beforeshow
1393          * Fires before this menu is displayed
1394          * @param {Roo.menu.Menu} this
1395          */
1396         beforeshow : true,
1397         /**
1398          * @event beforehide
1399          * Fires before this menu is hidden
1400          * @param {Roo.menu.Menu} this
1401          */
1402         beforehide : true,
1403         /**
1404          * @event show
1405          * Fires after this menu is displayed
1406          * @param {Roo.menu.Menu} this
1407          */
1408         show : true,
1409         /**
1410          * @event hide
1411          * Fires after this menu is hidden
1412          * @param {Roo.menu.Menu} this
1413          */
1414         hide : true,
1415         /**
1416          * @event click
1417          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1418          * @param {Roo.menu.Menu} this
1419          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1420          * @param {Roo.EventObject} e
1421          */
1422         click : true,
1423         /**
1424          * @event mouseover
1425          * Fires when the mouse is hovering over this menu
1426          * @param {Roo.menu.Menu} this
1427          * @param {Roo.EventObject} e
1428          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1429          */
1430         mouseover : true,
1431         /**
1432          * @event mouseout
1433          * Fires when the mouse exits this menu
1434          * @param {Roo.menu.Menu} this
1435          * @param {Roo.EventObject} e
1436          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1437          */
1438         mouseout : true,
1439         /**
1440          * @event itemclick
1441          * Fires when a menu item contained in this menu is clicked
1442          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1443          * @param {Roo.EventObject} e
1444          */
1445         itemclick: true
1446     });
1447     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1448 };
1449
1450 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1451     
1452    /// html : false,
1453     //align : '',
1454     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1455     type: false,
1456     /**
1457      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1458      */
1459     registerMenu : true,
1460     
1461     menuItems :false, // stores the menu items..
1462     
1463     hidden:true,
1464     
1465     parentMenu : false,
1466     
1467     getChildContainer : function() {
1468         return this.el;  
1469     },
1470     
1471     getAutoCreate : function(){
1472          
1473         //if (['right'].indexOf(this.align)!==-1) {
1474         //    cfg.cn[1].cls += ' pull-right'
1475         //}
1476         
1477         
1478         var cfg = {
1479             tag : 'ul',
1480             cls : 'dropdown-menu' ,
1481             style : 'z-index:1000'
1482             
1483         }
1484         
1485         if (this.type === 'submenu') {
1486             cfg.cls = 'submenu active';
1487         }
1488         if (this.type === 'treeview') {
1489             cfg.cls = 'treeview-menu';
1490         }
1491         
1492         return cfg;
1493     },
1494     initEvents : function() {
1495         
1496        // Roo.log("ADD event");
1497        // Roo.log(this.triggerEl.dom);
1498         this.triggerEl.on('click', this.onTriggerPress, this);
1499         this.triggerEl.addClass('dropdown-toggle');
1500         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1501
1502         this.el.on("mouseover", this.onMouseOver, this);
1503         this.el.on("mouseout", this.onMouseOut, this);
1504         
1505         
1506     },
1507     findTargetItem : function(e){
1508         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1509         if(!t){
1510             return false;
1511         }
1512         //Roo.log(t);         Roo.log(t.id);
1513         if(t && t.id){
1514             //Roo.log(this.menuitems);
1515             return this.menuitems.get(t.id);
1516             
1517             //return this.items.get(t.menuItemId);
1518         }
1519         
1520         return false;
1521     },
1522     onClick : function(e){
1523         Roo.log("menu.onClick");
1524         var t = this.findTargetItem(e);
1525         if(!t){
1526             return;
1527         }
1528         Roo.log(e);
1529         /*
1530         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1531             if(t == this.activeItem && t.shouldDeactivate(e)){
1532                 this.activeItem.deactivate();
1533                 delete this.activeItem;
1534                 return;
1535             }
1536             if(t.canActivate){
1537                 this.setActiveItem(t, true);
1538             }
1539             return;
1540             
1541             
1542         }
1543         */
1544         Roo.log('pass click event');
1545         
1546         t.onClick(e);
1547         
1548         this.fireEvent("click", this, t, e);
1549         
1550         this.hide();
1551     },
1552      onMouseOver : function(e){
1553         var t  = this.findTargetItem(e);
1554         //Roo.log(t);
1555         //if(t){
1556         //    if(t.canActivate && !t.disabled){
1557         //        this.setActiveItem(t, true);
1558         //    }
1559         //}
1560         
1561         this.fireEvent("mouseover", this, e, t);
1562     },
1563     isVisible : function(){
1564         return !this.hidden;
1565     },
1566      onMouseOut : function(e){
1567         var t  = this.findTargetItem(e);
1568         
1569         //if(t ){
1570         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1571         //        this.activeItem.deactivate();
1572         //        delete this.activeItem;
1573         //    }
1574         //}
1575         this.fireEvent("mouseout", this, e, t);
1576     },
1577     
1578     
1579     /**
1580      * Displays this menu relative to another element
1581      * @param {String/HTMLElement/Roo.Element} element The element to align to
1582      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1583      * the element (defaults to this.defaultAlign)
1584      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1585      */
1586     show : function(el, pos, parentMenu){
1587         this.parentMenu = parentMenu;
1588         if(!this.el){
1589             this.render();
1590         }
1591         this.fireEvent("beforeshow", this);
1592         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1593     },
1594      /**
1595      * Displays this menu at a specific xy position
1596      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1597      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1598      */
1599     showAt : function(xy, parentMenu, /* private: */_e){
1600         this.parentMenu = parentMenu;
1601         if(!this.el){
1602             this.render();
1603         }
1604         if(_e !== false){
1605             this.fireEvent("beforeshow", this);
1606             
1607             //xy = this.el.adjustForConstraints(xy);
1608         }
1609         //this.el.setXY(xy);
1610         //this.el.show();
1611         this.hideMenuItems();
1612         this.hidden = false;
1613         this.triggerEl.addClass('open');
1614         this.focus();
1615         this.fireEvent("show", this);
1616     },
1617     
1618     focus : function(){
1619         return;
1620         if(!this.hidden){
1621             this.doFocus.defer(50, this);
1622         }
1623     },
1624
1625     doFocus : function(){
1626         if(!this.hidden){
1627             this.focusEl.focus();
1628         }
1629     },
1630
1631     /**
1632      * Hides this menu and optionally all parent menus
1633      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1634      */
1635     hide : function(deep){
1636         
1637         this.hideMenuItems();
1638         if(this.el && this.isVisible()){
1639             this.fireEvent("beforehide", this);
1640             if(this.activeItem){
1641                 this.activeItem.deactivate();
1642                 this.activeItem = null;
1643             }
1644             this.triggerEl.removeClass('open');;
1645             this.hidden = true;
1646             this.fireEvent("hide", this);
1647         }
1648         if(deep === true && this.parentMenu){
1649             this.parentMenu.hide(true);
1650         }
1651     },
1652     
1653     onTriggerPress  : function(e)
1654     {
1655         
1656         Roo.log('trigger press');
1657         //Roo.log(e.getTarget());
1658        // Roo.log(this.triggerEl.dom);
1659         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1660             return;
1661         }
1662         if (this.isVisible()) {
1663             Roo.log('hide');
1664             this.hide();
1665         } else {
1666             this.show(this.triggerEl, false, false);
1667         }
1668         
1669         
1670     },
1671     
1672          
1673        
1674     
1675     hideMenuItems : function()
1676     {
1677         //$(backdrop).remove()
1678         Roo.select('.open',true).each(function(aa) {
1679             
1680             aa.removeClass('open');
1681           //var parent = getParent($(this))
1682           //var relatedTarget = { relatedTarget: this }
1683           
1684            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1685           //if (e.isDefaultPrevented()) return
1686            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1687         })
1688     },
1689     addxtypeChild : function (tree, cntr) {
1690         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1691           
1692         this.menuitems.add(comp);
1693         return comp;
1694
1695     },
1696     getEl : function()
1697     {
1698         Roo.log(this.el);
1699         return this.el;
1700     }
1701 });
1702
1703  
1704  /*
1705  * - LGPL
1706  *
1707  * menu item
1708  * 
1709  */
1710
1711
1712 /**
1713  * @class Roo.bootstrap.MenuItem
1714  * @extends Roo.bootstrap.Component
1715  * Bootstrap MenuItem class
1716  * @cfg {String} html the menu label
1717  * @cfg {String} href the link
1718  * @cfg {Boolean} preventDefault (true | false) default true
1719  * 
1720  * 
1721  * @constructor
1722  * Create a new MenuItem
1723  * @param {Object} config The config object
1724  */
1725
1726
1727 Roo.bootstrap.MenuItem = function(config){
1728     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1729     this.addEvents({
1730         // raw events
1731         /**
1732          * @event click
1733          * The raw click event for the entire grid.
1734          * @param {Roo.EventObject} e
1735          */
1736         "click" : true
1737     });
1738 };
1739
1740 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1741     
1742     href : false,
1743     html : false,
1744     preventDefault: true,
1745     
1746     getAutoCreate : function(){
1747         var cfg= {
1748             tag: 'li',
1749             cls: 'dropdown-menu-item',
1750             cn: [
1751                     {
1752                         tag : 'a',
1753                         href : '#',
1754                         html : 'Link'
1755                     }
1756                 ]
1757         };
1758         if (this.parent().type == 'treeview') {
1759             cfg.cls = 'treeview-menu';
1760         }
1761         
1762         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1763         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         //this.el.select('a').on('click', this.onClick, this);
1770         
1771     },
1772     onClick : function(e)
1773     {
1774         Roo.log('item on click ');
1775         //if(this.preventDefault){
1776         //    e.preventDefault();
1777         //}
1778         //this.parent().hideMenuItems();
1779         
1780         this.fireEvent('click', this, e);
1781     },
1782     getEl : function()
1783     {
1784         return this.el;
1785     }
1786 });
1787
1788  
1789
1790  /*
1791  * - LGPL
1792  *
1793  * menu separator
1794  * 
1795  */
1796
1797
1798 /**
1799  * @class Roo.bootstrap.MenuSeparator
1800  * @extends Roo.bootstrap.Component
1801  * Bootstrap MenuSeparator class
1802  * 
1803  * @constructor
1804  * Create a new MenuItem
1805  * @param {Object} config The config object
1806  */
1807
1808
1809 Roo.bootstrap.MenuSeparator = function(config){
1810     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1811 };
1812
1813 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1814     
1815     getAutoCreate : function(){
1816         var cfg = {
1817             cls: 'divider',
1818             tag : 'li'
1819         };
1820         
1821         return cfg;
1822     }
1823    
1824 });
1825
1826  
1827
1828  
1829 /*
1830 <div class="modal fade">
1831   <div class="modal-dialog">
1832     <div class="modal-content">
1833       <div class="modal-header">
1834         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1835         <h4 class="modal-title">Modal title</h4>
1836       </div>
1837       <div class="modal-body">
1838         <p>One fine body&hellip;</p>
1839       </div>
1840       <div class="modal-footer">
1841         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1842         <button type="button" class="btn btn-primary">Save changes</button>
1843       </div>
1844     </div><!-- /.modal-content -->
1845   </div><!-- /.modal-dialog -->
1846 </div><!-- /.modal -->
1847 */
1848 /*
1849  * - LGPL
1850  *
1851  * page contgainer.
1852  * 
1853  */
1854
1855 /**
1856  * @class Roo.bootstrap.Modal
1857  * @extends Roo.bootstrap.Component
1858  * Bootstrap Modal class
1859  * @cfg {String} title Title of dialog
1860  * @cfg {Boolean} specificTitle (true|false) default false
1861  * @cfg {Array} buttons Array of buttons or standard button set..
1862  * @cfg {String} buttonPosition (left|right|center) default right
1863  * 
1864  * @constructor
1865  * Create a new Modal Dialog
1866  * @param {Object} config The config object
1867  */
1868
1869 Roo.bootstrap.Modal = function(config){
1870     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1871     this.addEvents({
1872         // raw events
1873         /**
1874          * @event btnclick
1875          * The raw btnclick event for the button
1876          * @param {Roo.EventObject} e
1877          */
1878         "btnclick" : true
1879     });
1880     this.buttons = this.buttons || [];
1881 };
1882
1883 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1884     
1885     title : 'test dialog',
1886    
1887     buttons : false,
1888     
1889     // set on load...
1890     body:  false,
1891     
1892     specificTitle: false,
1893     
1894     buttonPosition: 'right',
1895     
1896     onRender : function(ct, position)
1897     {
1898         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1899      
1900         if(!this.el){
1901             var cfg = Roo.apply({},  this.getAutoCreate());
1902             cfg.id = Roo.id();
1903             //if(!cfg.name){
1904             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1905             //}
1906             //if (!cfg.name.length) {
1907             //    delete cfg.name;
1908            // }
1909             if (this.cls) {
1910                 cfg.cls += ' ' + this.cls;
1911             }
1912             if (this.style) {
1913                 cfg.style = this.style;
1914             }
1915             this.el = Roo.get(document.body).createChild(cfg, position);
1916         }
1917         //var type = this.el.dom.type;
1918         
1919         if(this.tabIndex !== undefined){
1920             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1921         }
1922         
1923         
1924         
1925         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1926         this.maskEl.enableDisplayMode("block");
1927         this.maskEl.hide();
1928         //this.el.addClass("x-dlg-modal");
1929     
1930         if (this.buttons.length) {
1931             Roo.each(this.buttons, function(bb) {
1932                 b = Roo.apply({}, bb);
1933                 b.xns = b.xns || Roo.bootstrap;
1934                 b.xtype = b.xtype || 'Button';
1935                 if (typeof(b.listeners) == 'undefined') {
1936                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1937                 }
1938                 
1939                 var btn = Roo.factory(b);
1940                 
1941                 btn.onRender(this.el.select('.modal-footer div').first());
1942                 
1943             },this);
1944         }
1945         // render the children.
1946         var nitems = [];
1947         
1948         if(typeof(this.items) != 'undefined'){
1949             var items = this.items;
1950             delete this.items;
1951
1952             for(var i =0;i < items.length;i++) {
1953                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1954             }
1955         }
1956         
1957         this.items = nitems;
1958         
1959         this.body = this.el.select('.modal-body',true).first();
1960         this.close = this.el.select('.modal-header .close', true).first();
1961         this.footer = this.el.select('.modal-footer',true).first();
1962         this.initEvents();
1963         //this.el.addClass([this.fieldClass, this.cls]);
1964         
1965     },
1966     getAutoCreate : function(){
1967         
1968         
1969         var bdy = {
1970                 cls : 'modal-body',
1971                 html : this.html || ''
1972         };
1973         
1974         var title = {
1975             tag: 'h4',
1976             cls : 'modal-title',
1977             html : this.title
1978         };
1979         
1980         if(this.specificTitle){
1981             title = this.title;
1982         };
1983         
1984         return modal = {
1985             cls: "modal fade",
1986             style : 'display: none',
1987             cn : [
1988                 {
1989                     cls: "modal-dialog",
1990                     cn : [
1991                         {
1992                             cls : "modal-content",
1993                             cn : [
1994                                 {
1995                                     cls : 'modal-header',
1996                                     cn : [
1997                                         {
1998                                             tag: 'button',
1999                                             cls : 'close',
2000                                             html : '&times'
2001                                         },
2002                                         title
2003                                     ]
2004                                 },
2005                                 bdy,
2006                                 {
2007                                     cls : 'modal-footer',
2008                                     cn : [
2009                                         {
2010                                             tag: 'div',
2011                                             cls: 'btn-' + this.buttonPosition
2012                                         }
2013                                     ]
2014                                     
2015                                 }
2016                                 
2017                                 
2018                             ]
2019                             
2020                         }
2021                     ]
2022                         
2023                 }
2024             ]
2025             
2026             
2027         };
2028           
2029     },
2030     getChildContainer : function() {
2031          
2032          return this.el.select('.modal-body',true).first();
2033         
2034     },
2035     getButtonContainer : function() {
2036          return this.el.select('.modal-footer div',true).first();
2037         
2038     },
2039     initEvents : function()
2040     {
2041         this.el.select('.modal-header .close').on('click', this.hide, this);
2042 //        
2043 //        this.addxtype(this);
2044     },
2045     show : function() {
2046         
2047         if (!this.rendered) {
2048             this.render();
2049         }
2050        
2051         this.el.addClass('on');
2052         this.el.removeClass('fade');
2053         this.el.setStyle('display', 'block');
2054         Roo.get(document.body).addClass("x-body-masked");
2055         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2056         this.maskEl.show();
2057         this.el.setStyle('zIndex', '10001');
2058         this.fireEvent('show', this);
2059         
2060         
2061     },
2062     hide : function()
2063     {
2064         Roo.log('Modal hide?!');
2065         this.maskEl.hide();
2066         Roo.get(document.body).removeClass("x-body-masked");
2067         this.el.removeClass('on');
2068         this.el.addClass('fade');
2069         this.el.setStyle('display', 'none');
2070         this.fireEvent('hide', this);
2071     },
2072     
2073     addButton : function(str, cb)
2074     {
2075          
2076         
2077         var b = Roo.apply({}, { html : str } );
2078         b.xns = b.xns || Roo.bootstrap;
2079         b.xtype = b.xtype || 'Button';
2080         if (typeof(b.listeners) == 'undefined') {
2081             b.listeners = { click : cb.createDelegate(this)  };
2082         }
2083         
2084         var btn = Roo.factory(b);
2085            
2086         btn.onRender(this.el.select('.modal-footer div').first());
2087         
2088         return btn;   
2089        
2090     },
2091     
2092     setDefaultButton : function(btn)
2093     {
2094         //this.el.select('.modal-footer').()
2095     },
2096     resizeTo: function(w,h)
2097     {
2098         // skip..
2099     },
2100     setContentSize  : function(w, h)
2101     {
2102         
2103     },
2104     onButtonClick: function(btn,e)
2105     {
2106         //Roo.log([a,b,c]);
2107         this.fireEvent('btnclick', btn.name, e);
2108     },
2109     setTitle: function(str) {
2110         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2111         
2112     }
2113 });
2114
2115
2116 Roo.apply(Roo.bootstrap.Modal,  {
2117     /**
2118          * Button config that displays a single OK button
2119          * @type Object
2120          */
2121         OK :  [{
2122             name : 'ok',
2123             weight : 'primary',
2124             html : 'OK'
2125         }], 
2126         /**
2127          * Button config that displays Yes and No buttons
2128          * @type Object
2129          */
2130         YESNO : [
2131             {
2132                 name  : 'no',
2133                 html : 'No'
2134             },
2135             {
2136                 name  :'yes',
2137                 weight : 'primary',
2138                 html : 'Yes'
2139             }
2140         ],
2141         
2142         /**
2143          * Button config that displays OK and Cancel buttons
2144          * @type Object
2145          */
2146         OKCANCEL : [
2147             {
2148                name : 'cancel',
2149                 html : 'Cancel'
2150             },
2151             {
2152                 name : 'ok',
2153                 weight : 'primary',
2154                 html : 'OK'
2155             }
2156         ],
2157         /**
2158          * Button config that displays Yes, No and Cancel buttons
2159          * @type Object
2160          */
2161         YESNOCANCEL : [
2162             {
2163                 name : 'yes',
2164                 weight : 'primary',
2165                 html : 'Yes'
2166             },
2167             {
2168                 name : 'no',
2169                 html : 'No'
2170             },
2171             {
2172                 name : 'cancel',
2173                 html : 'Cancel'
2174             }
2175         ]
2176 });
2177  /*
2178  * - LGPL
2179  *
2180  * messagebox - can be used as a replace
2181  * 
2182  */
2183 /**
2184  * @class Roo.MessageBox
2185  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2186  * Example usage:
2187  *<pre><code>
2188 // Basic alert:
2189 Roo.Msg.alert('Status', 'Changes saved successfully.');
2190
2191 // Prompt for user data:
2192 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2193     if (btn == 'ok'){
2194         // process text value...
2195     }
2196 });
2197
2198 // Show a dialog using config options:
2199 Roo.Msg.show({
2200    title:'Save Changes?',
2201    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2202    buttons: Roo.Msg.YESNOCANCEL,
2203    fn: processResult,
2204    animEl: 'elId'
2205 });
2206 </code></pre>
2207  * @singleton
2208  */
2209 Roo.bootstrap.MessageBox = function(){
2210     var dlg, opt, mask, waitTimer;
2211     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2212     var buttons, activeTextEl, bwidth;
2213
2214     
2215     // private
2216     var handleButton = function(button){
2217         dlg.hide();
2218         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2219     };
2220
2221     // private
2222     var handleHide = function(){
2223         if(opt && opt.cls){
2224             dlg.el.removeClass(opt.cls);
2225         }
2226         //if(waitTimer){
2227         //    Roo.TaskMgr.stop(waitTimer);
2228         //    waitTimer = null;
2229         //}
2230     };
2231
2232     // private
2233     var updateButtons = function(b){
2234         var width = 0;
2235         if(!b){
2236             buttons["ok"].hide();
2237             buttons["cancel"].hide();
2238             buttons["yes"].hide();
2239             buttons["no"].hide();
2240             //dlg.footer.dom.style.display = 'none';
2241             return width;
2242         }
2243         dlg.footer.dom.style.display = '';
2244         for(var k in buttons){
2245             if(typeof buttons[k] != "function"){
2246                 if(b[k]){
2247                     buttons[k].show();
2248                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2249                     width += buttons[k].el.getWidth()+15;
2250                 }else{
2251                     buttons[k].hide();
2252                 }
2253             }
2254         }
2255         return width;
2256     };
2257
2258     // private
2259     var handleEsc = function(d, k, e){
2260         if(opt && opt.closable !== false){
2261             dlg.hide();
2262         }
2263         if(e){
2264             e.stopEvent();
2265         }
2266     };
2267
2268     return {
2269         /**
2270          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2271          * @return {Roo.BasicDialog} The BasicDialog element
2272          */
2273         getDialog : function(){
2274            if(!dlg){
2275                 dlg = new Roo.bootstrap.Modal( {
2276                     //draggable: true,
2277                     //resizable:false,
2278                     //constraintoviewport:false,
2279                     //fixedcenter:true,
2280                     //collapsible : false,
2281                     //shim:true,
2282                     //modal: true,
2283                   //  width:400,
2284                   //  height:100,
2285                     //buttonAlign:"center",
2286                     closeClick : function(){
2287                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2288                             handleButton("no");
2289                         }else{
2290                             handleButton("cancel");
2291                         }
2292                     }
2293                 });
2294                 dlg.render();
2295                 dlg.on("hide", handleHide);
2296                 mask = dlg.mask;
2297                 //dlg.addKeyListener(27, handleEsc);
2298                 buttons = {};
2299                 this.buttons = buttons;
2300                 var bt = this.buttonText;
2301                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2302                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2303                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2304                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2305                 Roo.log(buttons)
2306                 bodyEl = dlg.body.createChild({
2307
2308                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2309                         '<textarea class="roo-mb-textarea"></textarea>' +
2310                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2311                 });
2312                 msgEl = bodyEl.dom.firstChild;
2313                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2314                 textboxEl.enableDisplayMode();
2315                 textboxEl.addKeyListener([10,13], function(){
2316                     if(dlg.isVisible() && opt && opt.buttons){
2317                         if(opt.buttons.ok){
2318                             handleButton("ok");
2319                         }else if(opt.buttons.yes){
2320                             handleButton("yes");
2321                         }
2322                     }
2323                 });
2324                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2325                 textareaEl.enableDisplayMode();
2326                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2327                 progressEl.enableDisplayMode();
2328                 var pf = progressEl.dom.firstChild;
2329                 if (pf) {
2330                     pp = Roo.get(pf.firstChild);
2331                     pp.setHeight(pf.offsetHeight);
2332                 }
2333                 
2334             }
2335             return dlg;
2336         },
2337
2338         /**
2339          * Updates the message box body text
2340          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2341          * the XHTML-compliant non-breaking space character '&amp;#160;')
2342          * @return {Roo.MessageBox} This message box
2343          */
2344         updateText : function(text){
2345             if(!dlg.isVisible() && !opt.width){
2346                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2347             }
2348             msgEl.innerHTML = text || '&#160;';
2349       
2350             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2351             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2352             var w = Math.max(
2353                     Math.min(opt.width || cw , this.maxWidth), 
2354                     Math.max(opt.minWidth || this.minWidth, bwidth)
2355             );
2356             if(opt.prompt){
2357                 activeTextEl.setWidth(w);
2358             }
2359             if(dlg.isVisible()){
2360                 dlg.fixedcenter = false;
2361             }
2362             // to big, make it scroll. = But as usual stupid IE does not support
2363             // !important..
2364             
2365             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2366                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2367                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2368             } else {
2369                 bodyEl.dom.style.height = '';
2370                 bodyEl.dom.style.overflowY = '';
2371             }
2372             if (cw > w) {
2373                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2374             } else {
2375                 bodyEl.dom.style.overflowX = '';
2376             }
2377             
2378             dlg.setContentSize(w, bodyEl.getHeight());
2379             if(dlg.isVisible()){
2380                 dlg.fixedcenter = true;
2381             }
2382             return this;
2383         },
2384
2385         /**
2386          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2387          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2388          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2389          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2390          * @return {Roo.MessageBox} This message box
2391          */
2392         updateProgress : function(value, text){
2393             if(text){
2394                 this.updateText(text);
2395             }
2396             if (pp) { // weird bug on my firefox - for some reason this is not defined
2397                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2398             }
2399             return this;
2400         },        
2401
2402         /**
2403          * Returns true if the message box is currently displayed
2404          * @return {Boolean} True if the message box is visible, else false
2405          */
2406         isVisible : function(){
2407             return dlg && dlg.isVisible();  
2408         },
2409
2410         /**
2411          * Hides the message box if it is displayed
2412          */
2413         hide : function(){
2414             if(this.isVisible()){
2415                 dlg.hide();
2416             }  
2417         },
2418
2419         /**
2420          * Displays a new message box, or reinitializes an existing message box, based on the config options
2421          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2422          * The following config object properties are supported:
2423          * <pre>
2424 Property    Type             Description
2425 ----------  ---------------  ------------------------------------------------------------------------------------
2426 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2427                                    closes (defaults to undefined)
2428 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2429                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2430 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2431                                    progress and wait dialogs will ignore this property and always hide the
2432                                    close button as they can only be closed programmatically.
2433 cls               String           A custom CSS class to apply to the message box element
2434 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2435                                    displayed (defaults to 75)
2436 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2437                                    function will be btn (the name of the button that was clicked, if applicable,
2438                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2439                                    Progress and wait dialogs will ignore this option since they do not respond to
2440                                    user actions and can only be closed programmatically, so any required function
2441                                    should be called by the same code after it closes the dialog.
2442 icon              String           A CSS class that provides a background image to be used as an icon for
2443                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2444 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2445 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2446 modal             Boolean          False to allow user interaction with the page while the message box is
2447                                    displayed (defaults to true)
2448 msg               String           A string that will replace the existing message box body text (defaults
2449                                    to the XHTML-compliant non-breaking space character '&#160;')
2450 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2451 progress          Boolean          True to display a progress bar (defaults to false)
2452 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2453 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2454 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2455 title             String           The title text
2456 value             String           The string value to set into the active textbox element if displayed
2457 wait              Boolean          True to display a progress bar (defaults to false)
2458 width             Number           The width of the dialog in pixels
2459 </pre>
2460          *
2461          * Example usage:
2462          * <pre><code>
2463 Roo.Msg.show({
2464    title: 'Address',
2465    msg: 'Please enter your address:',
2466    width: 300,
2467    buttons: Roo.MessageBox.OKCANCEL,
2468    multiline: true,
2469    fn: saveAddress,
2470    animEl: 'addAddressBtn'
2471 });
2472 </code></pre>
2473          * @param {Object} config Configuration options
2474          * @return {Roo.MessageBox} This message box
2475          */
2476         show : function(options)
2477         {
2478             
2479             // this causes nightmares if you show one dialog after another
2480             // especially on callbacks..
2481              
2482             if(this.isVisible()){
2483                 
2484                 this.hide();
2485                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2486                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2487                 Roo.log("New Dialog Message:" +  options.msg )
2488                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2489                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2490                 
2491             }
2492             var d = this.getDialog();
2493             opt = options;
2494             d.setTitle(opt.title || "&#160;");
2495             d.close.setDisplayed(opt.closable !== false);
2496             activeTextEl = textboxEl;
2497             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2498             if(opt.prompt){
2499                 if(opt.multiline){
2500                     textboxEl.hide();
2501                     textareaEl.show();
2502                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2503                         opt.multiline : this.defaultTextHeight);
2504                     activeTextEl = textareaEl;
2505                 }else{
2506                     textboxEl.show();
2507                     textareaEl.hide();
2508                 }
2509             }else{
2510                 textboxEl.hide();
2511                 textareaEl.hide();
2512             }
2513             progressEl.setDisplayed(opt.progress === true);
2514             this.updateProgress(0);
2515             activeTextEl.dom.value = opt.value || "";
2516             if(opt.prompt){
2517                 dlg.setDefaultButton(activeTextEl);
2518             }else{
2519                 var bs = opt.buttons;
2520                 var db = null;
2521                 if(bs && bs.ok){
2522                     db = buttons["ok"];
2523                 }else if(bs && bs.yes){
2524                     db = buttons["yes"];
2525                 }
2526                 dlg.setDefaultButton(db);
2527             }
2528             bwidth = updateButtons(opt.buttons);
2529             this.updateText(opt.msg);
2530             if(opt.cls){
2531                 d.el.addClass(opt.cls);
2532             }
2533             d.proxyDrag = opt.proxyDrag === true;
2534             d.modal = opt.modal !== false;
2535             d.mask = opt.modal !== false ? mask : false;
2536             if(!d.isVisible()){
2537                 // force it to the end of the z-index stack so it gets a cursor in FF
2538                 document.body.appendChild(dlg.el.dom);
2539                 d.animateTarget = null;
2540                 d.show(options.animEl);
2541             }
2542             return this;
2543         },
2544
2545         /**
2546          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2547          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2548          * and closing the message box when the process is complete.
2549          * @param {String} title The title bar text
2550          * @param {String} msg The message box body text
2551          * @return {Roo.MessageBox} This message box
2552          */
2553         progress : function(title, msg){
2554             this.show({
2555                 title : title,
2556                 msg : msg,
2557                 buttons: false,
2558                 progress:true,
2559                 closable:false,
2560                 minWidth: this.minProgressWidth,
2561                 modal : true
2562             });
2563             return this;
2564         },
2565
2566         /**
2567          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2568          * If a callback function is passed it will be called after the user clicks the button, and the
2569          * id of the button that was clicked will be passed as the only parameter to the callback
2570          * (could also be the top-right close button).
2571          * @param {String} title The title bar text
2572          * @param {String} msg The message box body text
2573          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2574          * @param {Object} scope (optional) The scope of the callback function
2575          * @return {Roo.MessageBox} This message box
2576          */
2577         alert : function(title, msg, fn, scope){
2578             this.show({
2579                 title : title,
2580                 msg : msg,
2581                 buttons: this.OK,
2582                 fn: fn,
2583                 scope : scope,
2584                 modal : true
2585             });
2586             return this;
2587         },
2588
2589         /**
2590          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2591          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2592          * You are responsible for closing the message box when the process is complete.
2593          * @param {String} msg The message box body text
2594          * @param {String} title (optional) The title bar text
2595          * @return {Roo.MessageBox} This message box
2596          */
2597         wait : function(msg, title){
2598             this.show({
2599                 title : title,
2600                 msg : msg,
2601                 buttons: false,
2602                 closable:false,
2603                 progress:true,
2604                 modal:true,
2605                 width:300,
2606                 wait:true
2607             });
2608             waitTimer = Roo.TaskMgr.start({
2609                 run: function(i){
2610                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2611                 },
2612                 interval: 1000
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2619          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2620          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2621          * @param {String} title The title bar text
2622          * @param {String} msg The message box body text
2623          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2624          * @param {Object} scope (optional) The scope of the callback function
2625          * @return {Roo.MessageBox} This message box
2626          */
2627         confirm : function(title, msg, fn, scope){
2628             this.show({
2629                 title : title,
2630                 msg : msg,
2631                 buttons: this.YESNO,
2632                 fn: fn,
2633                 scope : scope,
2634                 modal : true
2635             });
2636             return this;
2637         },
2638
2639         /**
2640          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2641          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2642          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2643          * (could also be the top-right close button) and the text that was entered will be passed as the two
2644          * parameters to the callback.
2645          * @param {String} title The title bar text
2646          * @param {String} msg The message box body text
2647          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2648          * @param {Object} scope (optional) The scope of the callback function
2649          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2650          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2651          * @return {Roo.MessageBox} This message box
2652          */
2653         prompt : function(title, msg, fn, scope, multiline){
2654             this.show({
2655                 title : title,
2656                 msg : msg,
2657                 buttons: this.OKCANCEL,
2658                 fn: fn,
2659                 minWidth:250,
2660                 scope : scope,
2661                 prompt:true,
2662                 multiline: multiline,
2663                 modal : true
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Button config that displays a single OK button
2670          * @type Object
2671          */
2672         OK : {ok:true},
2673         /**
2674          * Button config that displays Yes and No buttons
2675          * @type Object
2676          */
2677         YESNO : {yes:true, no:true},
2678         /**
2679          * Button config that displays OK and Cancel buttons
2680          * @type Object
2681          */
2682         OKCANCEL : {ok:true, cancel:true},
2683         /**
2684          * Button config that displays Yes, No and Cancel buttons
2685          * @type Object
2686          */
2687         YESNOCANCEL : {yes:true, no:true, cancel:true},
2688
2689         /**
2690          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2691          * @type Number
2692          */
2693         defaultTextHeight : 75,
2694         /**
2695          * The maximum width in pixels of the message box (defaults to 600)
2696          * @type Number
2697          */
2698         maxWidth : 600,
2699         /**
2700          * The minimum width in pixels of the message box (defaults to 100)
2701          * @type Number
2702          */
2703         minWidth : 100,
2704         /**
2705          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2706          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2707          * @type Number
2708          */
2709         minProgressWidth : 250,
2710         /**
2711          * An object containing the default button text strings that can be overriden for localized language support.
2712          * Supported properties are: ok, cancel, yes and no.
2713          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2714          * @type Object
2715          */
2716         buttonText : {
2717             ok : "OK",
2718             cancel : "Cancel",
2719             yes : "Yes",
2720             no : "No"
2721         }
2722     };
2723 }();
2724
2725 /**
2726  * Shorthand for {@link Roo.MessageBox}
2727  */
2728 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2729 Roo.Msg = Roo.Msg || Roo.MessageBox;
2730 /*
2731  * - LGPL
2732  *
2733  * navbar
2734  * 
2735  */
2736
2737 /**
2738  * @class Roo.bootstrap.Navbar
2739  * @extends Roo.bootstrap.Component
2740  * Bootstrap Navbar class
2741
2742  * @constructor
2743  * Create a new Navbar
2744  * @param {Object} config The config object
2745  */
2746
2747
2748 Roo.bootstrap.Navbar = function(config){
2749     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2750     
2751 };
2752
2753 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2754     
2755     
2756    
2757     // private
2758     navItems : false,
2759     loadMask : false,
2760     
2761     
2762     getAutoCreate : function(){
2763         
2764         
2765         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2766         
2767     },
2768     
2769     initEvents :function ()
2770     {
2771         //Roo.log(this.el.select('.navbar-toggle',true));
2772         this.el.select('.navbar-toggle',true).on('click', function() {
2773            // Roo.log('click');
2774             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2775         }, this);
2776         
2777         var mark = {
2778             tag: "div",
2779             cls:"x-dlg-mask"
2780         }
2781         
2782         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2783         
2784         var size = this.el.getSize();
2785         this.maskEl.setSize(size.width, size.height);
2786         this.maskEl.enableDisplayMode("block");
2787         this.maskEl.hide();
2788         
2789         if(this.loadMask){
2790             this.maskEl.show();
2791         }
2792     },
2793     
2794     
2795     getChildContainer : function()
2796     {
2797         if (this.el.select('.collapse').getCount()) {
2798             return this.el.select('.collapse',true).first();
2799         }
2800         
2801         return this.el;
2802     },
2803     
2804     mask : function()
2805     {
2806         this.maskEl.show();
2807     },
2808     
2809     unmask : function()
2810     {
2811         this.maskEl.hide();
2812     }
2813     
2814     
2815     
2816 });
2817
2818
2819
2820  
2821
2822  /*
2823  * - LGPL
2824  *
2825  * navbar
2826  * 
2827  */
2828
2829 /**
2830  * @class Roo.bootstrap.NavSimplebar
2831  * @extends Roo.bootstrap.Navbar
2832  * Bootstrap Sidebar class
2833  *
2834  * @cfg {Boolean} inverse is inverted color
2835  * 
2836  * @cfg {String} type (nav | pills | tabs)
2837  * @cfg {Boolean} arrangement stacked | justified
2838  * @cfg {String} align (left | right) alignment
2839  * 
2840  * @cfg {Boolean} main (true|false) main nav bar? default false
2841  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2842  * 
2843  * @cfg {String} tag (header|footer|nav|div) default is nav 
2844
2845  * 
2846  * 
2847  * 
2848  * @constructor
2849  * Create a new Sidebar
2850  * @param {Object} config The config object
2851  */
2852
2853
2854 Roo.bootstrap.NavSimplebar = function(config){
2855     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2856 };
2857
2858 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2859     
2860     inverse: false,
2861     
2862     type: false,
2863     arrangement: '',
2864     align : false,
2865     
2866     
2867     
2868     main : false,
2869     
2870     
2871     tag : false,
2872     
2873     
2874     getAutoCreate : function(){
2875         
2876         
2877         var cfg = {
2878             tag : this.tag || 'div',
2879             cls : 'navbar'
2880         };
2881           
2882         
2883         cfg.cn = [
2884             {
2885                 cls: 'nav',
2886                 tag : 'ul'
2887             }
2888         ];
2889         
2890          
2891         this.type = this.type || 'nav';
2892         if (['tabs','pills'].indexOf(this.type)!==-1) {
2893             cfg.cn[0].cls += ' nav-' + this.type
2894         
2895         
2896         } else {
2897             if (this.type!=='nav') {
2898                 Roo.log('nav type must be nav/tabs/pills')
2899             }
2900             cfg.cn[0].cls += ' navbar-nav'
2901         }
2902         
2903         
2904         
2905         
2906         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2907             cfg.cn[0].cls += ' nav-' + this.arrangement;
2908         }
2909         
2910         
2911         if (this.align === 'right') {
2912             cfg.cn[0].cls += ' navbar-right';
2913         }
2914         
2915         if (this.inverse) {
2916             cfg.cls += ' navbar-inverse';
2917             
2918         }
2919         
2920         
2921         return cfg;
2922     
2923         
2924     }
2925     
2926     
2927     
2928 });
2929
2930
2931
2932  
2933
2934  
2935        /*
2936  * - LGPL
2937  *
2938  * navbar
2939  * 
2940  */
2941
2942 /**
2943  * @class Roo.bootstrap.NavHeaderbar
2944  * @extends Roo.bootstrap.NavSimplebar
2945  * Bootstrap Sidebar class
2946  *
2947  * @cfg {String} brand what is brand
2948  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2949  * @cfg {String} brand_href href of the brand
2950  * 
2951  * @constructor
2952  * Create a new Sidebar
2953  * @param {Object} config The config object
2954  */
2955
2956
2957 Roo.bootstrap.NavHeaderbar = function(config){
2958     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2959 };
2960
2961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2962     
2963     position: '',
2964     brand: '',
2965     brand_href: false,
2966     
2967     
2968     getAutoCreate : function(){
2969         
2970         
2971         
2972         var   cfg = {
2973             tag: this.nav || 'nav',
2974             cls: 'navbar',
2975             role: 'navigation',
2976             cn: [
2977                 {
2978                     tag: 'div',
2979                     cls: 'navbar-header',
2980                     cn: [
2981                         {
2982                         tag: 'button',
2983                         type: 'button',
2984                         cls: 'navbar-toggle',
2985                         'data-toggle': 'collapse',
2986                         cn: [
2987                             {
2988                                 tag: 'span',
2989                                 cls: 'sr-only',
2990                                 html: 'Toggle navigation'
2991                             },
2992                             {
2993                                 tag: 'span',
2994                                 cls: 'icon-bar'
2995                             },
2996                             {
2997                                 tag: 'span',
2998                                 cls: 'icon-bar'
2999                             },
3000                             {
3001                                 tag: 'span',
3002                                 cls: 'icon-bar'
3003                             }
3004                         ]
3005                         }
3006                     ]
3007                 },
3008                 {
3009                 tag: 'div',
3010                 cls: 'collapse navbar-collapse'
3011                 }
3012             ]
3013         };
3014         
3015         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3016         
3017         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3018             cfg.cls += ' navbar-' + this.position;
3019             
3020             // tag can override this..
3021             
3022             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3023         }
3024         
3025         if (this.brand !== '') {
3026             cfg.cn[0].cn.push({
3027                 tag: 'a',
3028                 href: this.brand_href ? this.brand_href : '#',
3029                 cls: 'navbar-brand',
3030                 cn: [
3031                 this.brand
3032                 ]
3033             });
3034         }
3035         
3036         if(this.main){
3037             cfg.cls += ' main-nav';
3038         }
3039         
3040         
3041         return cfg;
3042
3043         
3044     }
3045     
3046     
3047     
3048 });
3049
3050
3051
3052  
3053
3054  /*
3055  * - LGPL
3056  *
3057  * navbar
3058  * 
3059  */
3060
3061 /**
3062  * @class Roo.bootstrap.NavSidebar
3063  * @extends Roo.bootstrap.Navbar
3064  * Bootstrap Sidebar class
3065  * 
3066  * @constructor
3067  * Create a new Sidebar
3068  * @param {Object} config The config object
3069  */
3070
3071
3072 Roo.bootstrap.NavSidebar = function(config){
3073     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3074 };
3075
3076 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3077     
3078     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3079     
3080     getAutoCreate : function(){
3081         
3082         
3083         return  {
3084             tag: 'div',
3085             cls: 'sidebar sidebar-nav'
3086         };
3087     
3088         
3089     }
3090     
3091     
3092     
3093 });
3094
3095
3096
3097  
3098
3099  /*
3100  * - LGPL
3101  *
3102  * nav group
3103  * 
3104  */
3105
3106 /**
3107  * @class Roo.bootstrap.NavGroup
3108  * @extends Roo.bootstrap.Component
3109  * Bootstrap NavGroup class
3110  * @cfg {String} align left | right
3111  * @cfg {Boolean} inverse false | true
3112  * @cfg {String} type (nav|pills|tab) default nav
3113  * @cfg {String} navId - reference Id for navbar.
3114
3115  * 
3116  * @constructor
3117  * Create a new nav group
3118  * @param {Object} config The config object
3119  */
3120
3121 Roo.bootstrap.NavGroup = function(config){
3122     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3123     this.navItems = [];
3124     Roo.bootstrap.NavGroup.register(this);
3125      this.addEvents({
3126         /**
3127              * @event changed
3128              * Fires when the active item changes
3129              * @param {Roo.bootstrap.NavGroup} this
3130              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3131              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3132          */
3133         'changed': true
3134      });
3135     
3136 };
3137
3138 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3139     
3140     align: '',
3141     inverse: false,
3142     form: false,
3143     type: 'nav',
3144     navId : '',
3145     // private
3146     
3147     navItems : false,
3148     
3149     getAutoCreate : function()
3150     {
3151         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3152         
3153         cfg = {
3154             tag : 'ul',
3155             cls: 'nav' 
3156         }
3157         
3158         if (['tabs','pills'].indexOf(this.type)!==-1) {
3159             cfg.cls += ' nav-' + this.type
3160         } else {
3161             if (this.type!=='nav') {
3162                 Roo.log('nav type must be nav/tabs/pills')
3163             }
3164             cfg.cls += ' navbar-nav'
3165         }
3166         
3167         if (this.parent().sidebar) {
3168             cfg = {
3169                 tag: 'ul',
3170                 cls: 'dashboard-menu sidebar-menu'
3171             }
3172             
3173             return cfg;
3174         }
3175         
3176         if (this.form === true) {
3177             cfg = {
3178                 tag: 'form',
3179                 cls: 'navbar-form'
3180             }
3181             
3182             if (this.align === 'right') {
3183                 cfg.cls += ' navbar-right';
3184             } else {
3185                 cfg.cls += ' navbar-left';
3186             }
3187         }
3188         
3189         if (this.align === 'right') {
3190             cfg.cls += ' navbar-right';
3191         }
3192         
3193         if (this.inverse) {
3194             cfg.cls += ' navbar-inverse';
3195             
3196         }
3197         
3198         
3199         return cfg;
3200     },
3201     
3202     setActiveItem : function(item)
3203     {
3204         var prev = false;
3205         Roo.each(this.navItems, function(v){
3206             if (v == item) {
3207                 return ;
3208             }
3209             if (v.isActive()) {
3210                 v.setActive(false, true);
3211                 prev = v;
3212                 
3213             }
3214             
3215         });
3216
3217         item.setActive(true, true);
3218         this.fireEvent('changed', this, item, prev);
3219         
3220         
3221     },
3222     
3223     
3224     register : function(item)
3225     {
3226         this.navItems.push( item);
3227         item.navId = this.navId;
3228     
3229     },
3230     getNavItem: function(tabId)
3231     {
3232         var ret = false;
3233         Roo.each(this.navItems, function(e) {
3234             if (e.tabId == tabId) {
3235                ret =  e;
3236                return false;
3237             }
3238             return true;
3239             
3240         });
3241         return ret;
3242     }
3243 });
3244
3245  
3246 Roo.apply(Roo.bootstrap.NavGroup, {
3247     
3248     groups: {},
3249     
3250     register : function(navgrp)
3251     {
3252         this.groups[navgrp.navId] = navgrp;
3253         
3254     },
3255     get: function(navId) {
3256         return this.groups[navId];
3257     }
3258     
3259     
3260     
3261 });
3262
3263  /*
3264  * - LGPL
3265  *
3266  * row
3267  * 
3268  */
3269
3270 /**
3271  * @class Roo.bootstrap.Navbar.Item
3272  * @extends Roo.bootstrap.Component
3273  * Bootstrap Navbar.Button class
3274  * @cfg {String} href  link to
3275  * @cfg {String} html content of button
3276  * @cfg {String} badge text inside badge
3277  * @cfg {String} glyphicon name of glyphicon
3278  * @cfg {String} icon name of font awesome icon
3279  * @cfg {Boolean} active Is item active
3280  * @cfg {Boolean} preventDefault (true | false) default false
3281  * @cfg {String} tabId the tab that this item activates.
3282   
3283  * @constructor
3284  * Create a new Navbar Button
3285  * @param {Object} config The config object
3286  */
3287 Roo.bootstrap.Navbar.Item = function(config){
3288     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3289     this.addEvents({
3290         // raw events
3291         /**
3292          * @event click
3293          * The raw click event for the entire grid.
3294          * @param {Roo.EventObject} e
3295          */
3296         "click" : true,
3297          /**
3298             * @event changed
3299             * Fires when the active item active state changes
3300             * @param {Roo.bootstrap.Navbar.Item} this
3301             * @param {boolean} state the new state
3302              
3303          */
3304         'changed': true
3305     });
3306    
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     html: '',
3313     badge: '',
3314     icon: false,
3315     glyphicon: false,
3316     active: false,
3317     preventDefault : false,
3318     tabId : false,
3319     
3320     getAutoCreate : function(){
3321         
3322         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3323         
3324         if (this.parent().parent().sidebar === true) {
3325             cfg = {
3326                 tag: 'li',
3327                 cls: '',
3328                 cn: [
3329                     {
3330                     tag: 'p',
3331                     cls: ''
3332                     }
3333                 ]
3334             }
3335             
3336             if (this.html) {
3337                 cfg.cn[0].html = this.html;
3338             }
3339             
3340             if (this.active) {
3341                 this.cls += ' active';
3342             }
3343             
3344             if (this.menu) {
3345                 cfg.cn[0].cls += ' dropdown-toggle';
3346                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3347             }
3348             
3349             if (this.href) {
3350                 cfg.cn[0].tag = 'a',
3351                 cfg.cn[0].href = this.href;
3352             }
3353             
3354             if (this.glyphicon) {
3355                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3356             }
3357                 
3358             if (this.icon) {
3359                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3360             }
3361             
3362             return cfg;
3363         }
3364         
3365         cfg = {
3366             tag: 'li',
3367                 cls: 'nav-item'
3368         }
3369             
3370         if (this.active) {
3371             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3372         }
3373             
3374         cfg.cn = [
3375             {
3376                 tag: 'p',
3377                 html: 'Text'
3378             }
3379         ];
3380         
3381         if (this.glyphicon) {
3382             if(cfg.html){cfg.html = ' ' + this.html};
3383             cfg.cn=[
3384                 {
3385                     tag: 'span',
3386                     cls: 'glyphicon glyphicon-' + this.glyphicon
3387                 }
3388             ];
3389         }
3390         
3391         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3392         
3393         if (this.menu) {
3394             cfg.cn[0].tag='a';
3395             cfg.cn[0].href='#';
3396             cfg.cn[0].html += " <span class='caret'></span>";
3397         //}else if (!this.href) {
3398         //    cfg.cn[0].tag='p';
3399         //    cfg.cn[0].cls='navbar-text';
3400         } else {
3401             cfg.cn[0].tag='a';
3402             cfg.cn[0].href=this.href||'#';
3403             cfg.cn[0].html=this.html;
3404         }
3405         
3406         if (this.badge !== '') {
3407             
3408             cfg.cn[0].cn=[
3409             cfg.cn[0].html + ' ',
3410             {
3411                 tag: 'span',
3412                 cls: 'badge',
3413                 html: this.badge
3414             }
3415             ];
3416             cfg.cn[0].html=''
3417         }
3418          
3419         if (this.icon) {
3420             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3421         }
3422         
3423         return cfg;
3424     },
3425     initEvents: function() {
3426        // Roo.log('init events?');
3427        // Roo.log(this.el.dom);
3428         this.el.select('a',true).on('click', this.onClick, this);
3429         // at this point parent should be available..
3430         this.parent().register(this);
3431     },
3432     
3433     onClick : function(e)
3434     {
3435         if(this.preventDefault){
3436             e.preventDefault();
3437         }
3438         
3439         if(this.fireEvent('click', this, e) === false){
3440             return;
3441         };
3442         
3443         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3444              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3445                 this.parent().setActiveItem(this);
3446             }
3447             
3448             
3449             
3450         } 
3451     },
3452     
3453     isActive: function () {
3454         return this.active
3455     },
3456     setActive : function(state, fire)
3457     {
3458         this.active = state;
3459         if (!state ) {
3460             this.el.removeClass('active');
3461         } else if (!this.el.hasClass('active')) {
3462             this.el.addClass('active');
3463         }
3464         if (fire) {
3465             this.fireEvent('changed', this, state);
3466         }
3467         
3468         
3469     }
3470      // this should not be here...
3471  
3472 });
3473  
3474
3475  /*
3476  * - LGPL
3477  *
3478  * row
3479  * 
3480  */
3481
3482 /**
3483  * @class Roo.bootstrap.NavItem
3484  * @extends Roo.bootstrap.Component
3485  * Bootstrap Navbar.NavItem class
3486  * @cfg {String} href  link to
3487  * @cfg {String} html content of button
3488  * @cfg {String} badge text inside badge
3489  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3490  * @cfg {String} glyphicon name of glyphicon
3491  * @cfg {String} icon name of font awesome icon
3492  * @cfg {Boolean} active Is item active
3493  * @cfg {Boolean} preventDefault (true | false) default false
3494  * @cfg {String} tabId the tab that this item activates.
3495   
3496  * @constructor
3497  * Create a new Navbar Item
3498  * @param {Object} config The config object
3499  */
3500 Roo.bootstrap.NavItem = function(config){
3501     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3502     this.addEvents({
3503         // raw events
3504         /**
3505          * @event click
3506          * The raw click event for the entire grid.
3507          * @param {Roo.EventObject} e
3508          */
3509         "click" : true,
3510          /**
3511             * @event changed
3512             * Fires when the active item active state changes
3513             * @param {Roo.bootstrap.NavItem} this
3514             * @param {boolean} state the new state
3515              
3516          */
3517         'changed': true
3518     });
3519    
3520 };
3521
3522 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3523     
3524     href: false,
3525     html: '',
3526     badge: '',
3527     icon: false,
3528     glyphicon: false,
3529     active: false,
3530     preventDefault : false,
3531     tabId : false,
3532     
3533     getAutoCreate : function(){
3534          
3535         var cfg = {
3536             tag: 'li',
3537             cls: 'nav-item',
3538             cn : [
3539                 {
3540                     tag: 'a',
3541                     href : this.href || "#",
3542                     html: this.html || ''
3543                 }
3544             ]
3545         }
3546             
3547         if (this.active) {
3548             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3549         }
3550             
3551         // glyphicon and icon go before content..
3552         if (this.glyphicon || this.icon) {
3553              if (this.icon) {
3554                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html + '</span>'
3555             } else {
3556                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span>'  + cfg.cn[0].html;
3557             }
3558         }
3559         
3560         
3561         
3562         if (this.menu) {
3563             
3564             cfg.cn[0].html += " <span class='caret'></span>";
3565          
3566         }
3567         
3568         if (this.badge !== '') {
3569              
3570             cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3571         }
3572         
3573         
3574         
3575         return cfg;
3576     },
3577     initEvents: function() {
3578        // Roo.log('init events?');
3579        // Roo.log(this.el.dom);
3580         this.el.select('a',true).on('click', this.onClick, this);
3581         // at this point parent should be available..
3582         this.parent().register(this);
3583     },
3584     
3585     onClick : function(e)
3586     {
3587         if(this.preventDefault){
3588             e.preventDefault();
3589         }
3590         
3591         if(this.fireEvent('click', this, e) === false){
3592             return;
3593         };
3594         
3595         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3596              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3597                 this.parent().setActiveItem(this);
3598             }
3599             
3600             
3601             
3602         } 
3603     },
3604     
3605     isActive: function () {
3606         return this.active
3607     },
3608     setActive : function(state, fire)
3609     {
3610         this.active = state;
3611         if (!state ) {
3612             this.el.removeClass('active');
3613         } else if (!this.el.hasClass('active')) {
3614             this.el.addClass('active');
3615         }
3616         if (fire) {
3617             this.fireEvent('changed', this, state);
3618         }
3619         
3620         
3621     }
3622      // this should not be here...
3623  
3624 });
3625  
3626
3627  /*
3628  * - LGPL
3629  *
3630  * sidebar item
3631  *
3632  *  li
3633  *    <span> icon </span>
3634  *    <span> text </span>
3635  *    <span>badge </span>
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavSidebarItem
3640  * @extends Roo.bootstrap.Component
3641  * Bootstrap Navbar.NavSidebarItem class
3642  * @constructor
3643  * Create a new Navbar Button
3644  * @param {Object} config The config object
3645  */
3646 Roo.bootstrap.NavSidebarItem = function(config){
3647     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3648     this.addEvents({
3649         // raw events
3650         /**
3651          * @event click
3652          * The raw click event for the entire grid.
3653          * @param {Roo.EventObject} e
3654          */
3655         "click" : true,
3656          /**
3657             * @event changed
3658             * Fires when the active item active state changes
3659             * @param {Roo.bootstrap.Navbar.Item} this
3660             * @param {boolean} state the new state
3661              
3662          */
3663         'changed': true
3664     });
3665    
3666 };
3667
3668 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3669     
3670     
3671     getAutoCreate : function(){
3672         
3673         
3674         var a = {
3675                 tag: 'a',
3676                 href : this.href || '#',
3677                 cls: '',
3678                 html : '',
3679                 cn : []
3680         };
3681         var cfg = {
3682             tag: 'li',
3683             cls: '',
3684             cn: [ a ]
3685         }
3686         var span = {
3687             tag: 'span',
3688             html : this.html || ''
3689         }
3690         
3691         
3692         if (this.active) {
3693             cfg.cls += ' active';
3694         }
3695         
3696         // left icon..
3697         if (this.glyphicon || this.icon) {
3698             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3699             a.cn.push({ tag : 'i', cls : c }) ;
3700         }
3701         // html..
3702         a.cn.push(span);
3703         // then badge..
3704         if (this.badge !== '') {
3705             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3706         }
3707         // fi
3708         if (this.menu) {
3709             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3710             a.cls += 'dropdown-toggle treeview' ;
3711             
3712         }
3713         
3714         
3715         
3716         return cfg;
3717          
3718            
3719     }
3720    
3721      
3722  
3723 });
3724  
3725
3726  /*
3727  * - LGPL
3728  *
3729  * row
3730  * 
3731  */
3732
3733 /**
3734  * @class Roo.bootstrap.Row
3735  * @extends Roo.bootstrap.Component
3736  * Bootstrap Row class (contains columns...)
3737  * 
3738  * @constructor
3739  * Create a new Row
3740  * @param {Object} config The config object
3741  */
3742
3743 Roo.bootstrap.Row = function(config){
3744     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3745 };
3746
3747 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3748     
3749     getAutoCreate : function(){
3750        return {
3751             cls: 'row clearfix'
3752        };
3753     }
3754     
3755     
3756 });
3757
3758  
3759
3760  /*
3761  * - LGPL
3762  *
3763  * element
3764  * 
3765  */
3766
3767 /**
3768  * @class Roo.bootstrap.Element
3769  * @extends Roo.bootstrap.Component
3770  * Bootstrap Element class
3771  * @cfg {String} html contents of the element
3772  * @cfg {String} tag tag of the element
3773  * @cfg {String} cls class of the element
3774  * 
3775  * @constructor
3776  * Create a new Element
3777  * @param {Object} config The config object
3778  */
3779
3780 Roo.bootstrap.Element = function(config){
3781     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3782 };
3783
3784 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3785     
3786     tag: 'div',
3787     cls: '',
3788     html: '',
3789      
3790     
3791     getAutoCreate : function(){
3792         
3793         var cfg = {
3794             tag: this.tag,
3795             cls: this.cls,
3796             html: this.html
3797         }
3798         
3799         
3800         
3801         return cfg;
3802     }
3803    
3804 });
3805
3806  
3807
3808  /*
3809  * - LGPL
3810  *
3811  * pagination
3812  * 
3813  */
3814
3815 /**
3816  * @class Roo.bootstrap.Pagination
3817  * @extends Roo.bootstrap.Component
3818  * Bootstrap Pagination class
3819  * @cfg {String} size xs | sm | md | lg
3820  * @cfg {Boolean} inverse false | true
3821  * 
3822  * @constructor
3823  * Create a new Pagination
3824  * @param {Object} config The config object
3825  */
3826
3827 Roo.bootstrap.Pagination = function(config){
3828     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3829 };
3830
3831 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3832     
3833     cls: false,
3834     size: false,
3835     inverse: false,
3836     
3837     getAutoCreate : function(){
3838         var cfg = {
3839             tag: 'ul',
3840                 cls: 'pagination'
3841         };
3842         if (this.inverse) {
3843             cfg.cls += ' inverse';
3844         }
3845         if (this.html) {
3846             cfg.html=this.html;
3847         }
3848         if (this.cls) {
3849             cfg.cls += " " + this.cls;
3850         }
3851         return cfg;
3852     }
3853    
3854 });
3855
3856  
3857
3858  /*
3859  * - LGPL
3860  *
3861  * Pagination item
3862  * 
3863  */
3864
3865
3866 /**
3867  * @class Roo.bootstrap.PaginationItem
3868  * @extends Roo.bootstrap.Component
3869  * Bootstrap PaginationItem class
3870  * @cfg {String} html text
3871  * @cfg {String} href the link
3872  * @cfg {Boolean} preventDefault (true | false) default true
3873  * @cfg {Boolean} active (true | false) default false
3874  * 
3875  * 
3876  * @constructor
3877  * Create a new PaginationItem
3878  * @param {Object} config The config object
3879  */
3880
3881
3882 Roo.bootstrap.PaginationItem = function(config){
3883     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3884     this.addEvents({
3885         // raw events
3886         /**
3887          * @event click
3888          * The raw click event for the entire grid.
3889          * @param {Roo.EventObject} e
3890          */
3891         "click" : true
3892     });
3893 };
3894
3895 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3896     
3897     href : false,
3898     html : false,
3899     preventDefault: true,
3900     active : false,
3901     cls : false,
3902     
3903     getAutoCreate : function(){
3904         var cfg= {
3905             tag: 'li',
3906             cn: [
3907                 {
3908                     tag : 'a',
3909                     href : this.href ? this.href : '#',
3910                     html : this.html ? this.html : ''
3911                 }
3912             ]
3913         };
3914         
3915         if(this.cls){
3916             cfg.cls = this.cls;
3917         }
3918         
3919         if(this.active){
3920             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3921         }
3922         
3923         return cfg;
3924     },
3925     
3926     initEvents: function() {
3927         
3928         this.el.on('click', this.onClick, this);
3929         
3930     },
3931     onClick : function(e)
3932     {
3933         Roo.log('PaginationItem on click ');
3934         if(this.preventDefault){
3935             e.preventDefault();
3936         }
3937         
3938         this.fireEvent('click', this, e);
3939     }
3940    
3941 });
3942
3943  
3944
3945  /*
3946  * - LGPL
3947  *
3948  * slider
3949  * 
3950  */
3951
3952
3953 /**
3954  * @class Roo.bootstrap.Slider
3955  * @extends Roo.bootstrap.Component
3956  * Bootstrap Slider class
3957  *    
3958  * @constructor
3959  * Create a new Slider
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Slider = function(config){
3964     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3965 };
3966
3967 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3968     
3969     getAutoCreate : function(){
3970         
3971         var cfg = {
3972             tag: 'div',
3973             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3974             cn: [
3975                 {
3976                     tag: 'a',
3977                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3978                 }
3979             ]
3980         }
3981         
3982         return cfg;
3983     }
3984    
3985 });
3986
3987  /*
3988  * - LGPL
3989  *
3990  * table
3991  * 
3992  */
3993
3994 /**
3995  * @class Roo.bootstrap.Table
3996  * @extends Roo.bootstrap.Component
3997  * Bootstrap Table class
3998  * @cfg {String} cls table class
3999  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4000  * @cfg {String} bgcolor Specifies the background color for a table
4001  * @cfg {Number} border Specifies whether the table cells should have borders or not
4002  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4003  * @cfg {Number} cellspacing Specifies the space between cells
4004  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4005  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4006  * @cfg {String} sortable Specifies that the table should be sortable
4007  * @cfg {String} summary Specifies a summary of the content of a table
4008  * @cfg {Number} width Specifies the width of a table
4009  * 
4010  * @cfg {boolean} striped Should the rows be alternative striped
4011  * @cfg {boolean} bordered Add borders to the table
4012  * @cfg {boolean} hover Add hover highlighting
4013  * @cfg {boolean} condensed Format condensed
4014  * @cfg {boolean} responsive Format condensed
4015  *
4016  
4017  
4018  * 
4019  * @constructor
4020  * Create a new Table
4021  * @param {Object} config The config object
4022  */
4023
4024 Roo.bootstrap.Table = function(config){
4025     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4026     
4027     if (this.sm) {
4028         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4029         this.sm = this.selModel;
4030         this.sm.xmodule = this.xmodule || false;
4031     }
4032     if (this.cm && typeof(this.cm.config) == 'undefined') {
4033         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
4034         this.cm = this.colModel;
4035         this.cm.xmodule = this.xmodule || false;
4036     }
4037     if (this.store) {
4038         this.store= Roo.factory(this.store, Roo.data);
4039         this.ds = this.store;
4040         this.ds.xmodule = this.xmodule || false;
4041          
4042     }
4043 };
4044
4045 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4046     
4047     cls: false,
4048     align: false,
4049     bgcolor: false,
4050     border: false,
4051     cellpadding: false,
4052     cellspacing: false,
4053     frame: false,
4054     rules: false,
4055     sortable: false,
4056     summary: false,
4057     width: false,
4058     striped : false,
4059     bordered: false,
4060     hover:  false,
4061     condensed : false,
4062     responsive : false,
4063     sm : false,
4064     cm : false,
4065     store : false,
4066     
4067     getAutoCreate : function(){
4068         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4069         
4070         cfg = {
4071             tag: 'table',
4072             cls : 'table',
4073             cn : []
4074         }
4075             
4076         if (this.striped) {
4077             cfg.cls += ' table-striped';
4078         }
4079         if (this.hover) {
4080             cfg.cls += ' table-hover';
4081         }
4082         if (this.bordered) {
4083             cfg.cls += ' table-bordered';
4084         }
4085         if (this.condensed) {
4086             cfg.cls += ' table-condensed';
4087         }
4088         if (this.responsive) {
4089             cfg.cls += ' table-responsive';
4090         }
4091         
4092           
4093         
4094         
4095         if (this.cls) {
4096             cfg.cls+=  ' ' +this.cls;
4097         }
4098         
4099         // this lot should be simplifed...
4100         
4101         if (this.align) {
4102             cfg.align=this.align;
4103         }
4104         if (this.bgcolor) {
4105             cfg.bgcolor=this.bgcolor;
4106         }
4107         if (this.border) {
4108             cfg.border=this.border;
4109         }
4110         if (this.cellpadding) {
4111             cfg.cellpadding=this.cellpadding;
4112         }
4113         if (this.cellspacing) {
4114             cfg.cellspacing=this.cellspacing;
4115         }
4116         if (this.frame) {
4117             cfg.frame=this.frame;
4118         }
4119         if (this.rules) {
4120             cfg.rules=this.rules;
4121         }
4122         if (this.sortable) {
4123             cfg.sortable=this.sortable;
4124         }
4125         if (this.summary) {
4126             cfg.summary=this.summary;
4127         }
4128         if (this.width) {
4129             cfg.width=this.width;
4130         }
4131         
4132         if(this.store || this.cm){
4133             cfg.cn.push(this.renderHeader());
4134             cfg.cn.push(this.renderBody());
4135             cfg.cn.push(this.renderFooter());
4136             
4137             cfg.cls+=  ' TableGrid';
4138         }
4139         
4140         return cfg;
4141     },
4142 //    
4143 //    initTableGrid : function()
4144 //    {
4145 //        var cfg = {};
4146 //        
4147 //        var header = {
4148 //            tag: 'thead',
4149 //            cn : []
4150 //        };
4151 //        
4152 //        var cm = this.cm;
4153 //        
4154 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4155 //            header.cn.push({
4156 //                tag: 'th',
4157 //                html: cm.getColumnHeader(i)
4158 //            })
4159 //        }
4160 //        
4161 //        cfg.push(header);
4162 //        
4163 //        return cfg;
4164 //        
4165 //        
4166 //    },
4167     
4168     initEvents : function()
4169     {   
4170         if(!this.store || !this.cm){
4171             return;
4172         }
4173         
4174         Roo.log('initEvents with ds!!!!');
4175         
4176         var _this = this;
4177         
4178         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4179             e.on('click', _this.sort, _this);
4180         });
4181 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4182 //        this.maskEl.enableDisplayMode("block");
4183 //        this.maskEl.show();
4184         
4185         this.store.on('load', this.onLoad, this);
4186         this.store.on('beforeload', this.onBeforeLoad, this);
4187         
4188         this.store.load();
4189         
4190         
4191         
4192     },
4193     
4194     sort : function(e,el)
4195     {
4196         var col = Roo.get(el)
4197         
4198         if(!col.hasClass('sortable')){
4199             return;
4200         }
4201         
4202         var sort = col.attr('sort');
4203         var dir = 'ASC';
4204         
4205         if(col.hasClass('glyphicon-arrow-up')){
4206             dir = 'DESC';
4207         }
4208         
4209         this.store.sortInfo = {field : sort, direction : dir};
4210         
4211         this.store.load();
4212     },
4213     
4214     renderHeader : function()
4215     {
4216         var header = {
4217             tag: 'thead',
4218             cn : []
4219         };
4220         
4221         var cm = this.cm;
4222         
4223         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4224             
4225             var config = cm.config[i];
4226             
4227             var c = {
4228                 tag: 'th',
4229                 html: cm.getColumnHeader(i)
4230             };
4231             
4232             if(typeof(config.dataIndex) != 'undefined'){
4233                 c.sort = config.dataIndex;
4234             }
4235             
4236             if(typeof(config.sortable) != 'undefined' && config.sortable){
4237                 c.cls = 'sortable';
4238             }
4239             
4240             if(typeof(config.width) != 'undefined'){
4241                 c.style = 'width:' + config.width + 'px';
4242             }
4243             
4244             header.cn.push(c)
4245         }
4246         
4247         return header;
4248     },
4249     
4250     renderBody : function()
4251     {
4252         var body = {
4253             tag: 'tbody',
4254             cn : []
4255         };
4256         
4257         return body;
4258     },
4259     
4260     renderFooter : function()
4261     {
4262         var footer = {
4263             tag: 'tfoot',
4264             cn : []
4265         };
4266         
4267         return footer;
4268     },
4269     
4270     onLoad : function()
4271     {
4272         Roo.log('ds onload');
4273         
4274         var _this = this;
4275         var cm = this.cm;
4276         
4277         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4278             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4279             
4280             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4281                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4282             }
4283             
4284             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4285                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4286             }
4287         });
4288         
4289         var tbody = this.el.select('tbody', true).first();
4290         
4291         var renders = [];
4292         
4293         if(this.store.getCount() > 0){
4294             this.store.data.each(function(d){
4295                 var row = {
4296                     tag : 'tr',
4297                     cn : []
4298                 };
4299                 
4300                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4301                     var renderer = cm.getRenderer(i);
4302                     var config = cm.config[i];
4303                     var value = '';
4304                     var id = Roo.id();
4305                     
4306                     if(typeof(renderer) !== 'undefined'){
4307                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4308                     }
4309                     
4310                     if(typeof(value) === 'object'){
4311                         renders.push({
4312                             id : id,
4313                             cfg : value 
4314                         })
4315                     }
4316                     
4317                     var td = {
4318                         tag: 'td',
4319                         id: id,
4320                         html: (typeof(value) === 'object') ? '' : value
4321                     };
4322                     
4323                     if(typeof(config.width) != 'undefined'){
4324                         td.style = 'width:' +  config.width + 'px';
4325                     }
4326                     
4327                     row.cn.push(td);
4328                    
4329                 }
4330                 
4331                 tbody.createChild(row);
4332                 
4333             });
4334         }
4335         
4336         
4337         if(renders.length){
4338             var _this = this;
4339             Roo.each(renders, function(r){
4340                 _this.renderColumn(r);
4341             })
4342         }
4343 //        
4344 //        if(this.loadMask){
4345 //            this.maskEl.hide();
4346 //        }
4347     },
4348     
4349     onBeforeLoad : function()
4350     {
4351         Roo.log('ds onBeforeLoad');
4352         
4353         this.clear();
4354         
4355 //        if(this.loadMask){
4356 //            this.maskEl.show();
4357 //        }
4358     },
4359     
4360     clear : function()
4361     {
4362         this.el.select('tbody', true).first().dom.innerHTML = '';
4363     },
4364     
4365     getSelectionModel : function(){
4366         if(!this.selModel){
4367             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4368         }
4369         return this.selModel;
4370     },
4371     
4372     renderColumn : function(r)
4373     {
4374         var _this = this;
4375         r.cfg.render(Roo.get(r.id));
4376         
4377         if(r.cfg.cn){
4378             Roo.each(r.cfg.cn, function(c){
4379                 var child = {
4380                     id: r.id,
4381                     cfg: c
4382                 }
4383                 _this.renderColumn(child);
4384             })
4385         }
4386     }
4387    
4388 });
4389
4390  
4391
4392  /*
4393  * - LGPL
4394  *
4395  * table cell
4396  * 
4397  */
4398
4399 /**
4400  * @class Roo.bootstrap.TableCell
4401  * @extends Roo.bootstrap.Component
4402  * Bootstrap TableCell class
4403  * @cfg {String} html cell contain text
4404  * @cfg {String} cls cell class
4405  * @cfg {String} tag cell tag (td|th) default td
4406  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4407  * @cfg {String} align Aligns the content in a cell
4408  * @cfg {String} axis Categorizes cells
4409  * @cfg {String} bgcolor Specifies the background color of a cell
4410  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4411  * @cfg {Number} colspan Specifies the number of columns a cell should span
4412  * @cfg {String} headers Specifies one or more header cells a cell is related to
4413  * @cfg {Number} height Sets the height of a cell
4414  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4415  * @cfg {Number} rowspan Sets the number of rows a cell should span
4416  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4417  * @cfg {String} valign Vertical aligns the content in a cell
4418  * @cfg {Number} width Specifies the width of a cell
4419  * 
4420  * @constructor
4421  * Create a new TableCell
4422  * @param {Object} config The config object
4423  */
4424
4425 Roo.bootstrap.TableCell = function(config){
4426     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4427 };
4428
4429 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4430     
4431     html: false,
4432     cls: false,
4433     tag: false,
4434     abbr: false,
4435     align: false,
4436     axis: false,
4437     bgcolor: false,
4438     charoff: false,
4439     colspan: false,
4440     headers: false,
4441     height: false,
4442     nowrap: false,
4443     rowspan: false,
4444     scope: false,
4445     valign: false,
4446     width: false,
4447     
4448     
4449     getAutoCreate : function(){
4450         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4451         
4452         cfg = {
4453             tag: 'td'
4454         }
4455         
4456         if(this.tag){
4457             cfg.tag = this.tag;
4458         }
4459         
4460         if (this.html) {
4461             cfg.html=this.html
4462         }
4463         if (this.cls) {
4464             cfg.cls=this.cls
4465         }
4466         if (this.abbr) {
4467             cfg.abbr=this.abbr
4468         }
4469         if (this.align) {
4470             cfg.align=this.align
4471         }
4472         if (this.axis) {
4473             cfg.axis=this.axis
4474         }
4475         if (this.bgcolor) {
4476             cfg.bgcolor=this.bgcolor
4477         }
4478         if (this.charoff) {
4479             cfg.charoff=this.charoff
4480         }
4481         if (this.colspan) {
4482             cfg.colspan=this.colspan
4483         }
4484         if (this.headers) {
4485             cfg.headers=this.headers
4486         }
4487         if (this.height) {
4488             cfg.height=this.height
4489         }
4490         if (this.nowrap) {
4491             cfg.nowrap=this.nowrap
4492         }
4493         if (this.rowspan) {
4494             cfg.rowspan=this.rowspan
4495         }
4496         if (this.scope) {
4497             cfg.scope=this.scope
4498         }
4499         if (this.valign) {
4500             cfg.valign=this.valign
4501         }
4502         if (this.width) {
4503             cfg.width=this.width
4504         }
4505         
4506         
4507         return cfg;
4508     }
4509    
4510 });
4511
4512  
4513
4514  /*
4515  * - LGPL
4516  *
4517  * table row
4518  * 
4519  */
4520
4521 /**
4522  * @class Roo.bootstrap.TableRow
4523  * @extends Roo.bootstrap.Component
4524  * Bootstrap TableRow class
4525  * @cfg {String} cls row class
4526  * @cfg {String} align Aligns the content in a table row
4527  * @cfg {String} bgcolor Specifies a background color for a table row
4528  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4529  * @cfg {String} valign Vertical aligns the content in a table row
4530  * 
4531  * @constructor
4532  * Create a new TableRow
4533  * @param {Object} config The config object
4534  */
4535
4536 Roo.bootstrap.TableRow = function(config){
4537     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4538 };
4539
4540 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4541     
4542     cls: false,
4543     align: false,
4544     bgcolor: false,
4545     charoff: false,
4546     valign: false,
4547     
4548     getAutoCreate : function(){
4549         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4550         
4551         cfg = {
4552             tag: 'tr'
4553         }
4554             
4555         if(this.cls){
4556             cfg.cls = this.cls;
4557         }
4558         if(this.align){
4559             cfg.align = this.align;
4560         }
4561         if(this.bgcolor){
4562             cfg.bgcolor = this.bgcolor;
4563         }
4564         if(this.charoff){
4565             cfg.charoff = this.charoff;
4566         }
4567         if(this.valign){
4568             cfg.valign = this.valign;
4569         }
4570         
4571         return cfg;
4572     }
4573    
4574 });
4575
4576  
4577
4578  /*
4579  * - LGPL
4580  *
4581  * table body
4582  * 
4583  */
4584
4585 /**
4586  * @class Roo.bootstrap.TableBody
4587  * @extends Roo.bootstrap.Component
4588  * Bootstrap TableBody class
4589  * @cfg {String} cls element class
4590  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4591  * @cfg {String} align Aligns the content inside the element
4592  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4593  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4594  * 
4595  * @constructor
4596  * Create a new TableBody
4597  * @param {Object} config The config object
4598  */
4599
4600 Roo.bootstrap.TableBody = function(config){
4601     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4602 };
4603
4604 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4605     
4606     cls: false,
4607     tag: false,
4608     align: false,
4609     charoff: false,
4610     valign: false,
4611     
4612     getAutoCreate : function(){
4613         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4614         
4615         cfg = {
4616             tag: 'tbody'
4617         }
4618             
4619         if (this.cls) {
4620             cfg.cls=this.cls
4621         }
4622         if(this.tag){
4623             cfg.tag = this.tag;
4624         }
4625         
4626         if(this.align){
4627             cfg.align = this.align;
4628         }
4629         if(this.charoff){
4630             cfg.charoff = this.charoff;
4631         }
4632         if(this.valign){
4633             cfg.valign = this.valign;
4634         }
4635         
4636         return cfg;
4637     }
4638     
4639     
4640 //    initEvents : function()
4641 //    {
4642 //        
4643 //        if(!this.store){
4644 //            return;
4645 //        }
4646 //        
4647 //        this.store = Roo.factory(this.store, Roo.data);
4648 //        this.store.on('load', this.onLoad, this);
4649 //        
4650 //        this.store.load();
4651 //        
4652 //    },
4653 //    
4654 //    onLoad: function () 
4655 //    {   
4656 //        this.fireEvent('load', this);
4657 //    }
4658 //    
4659 //   
4660 });
4661
4662  
4663
4664  /*
4665  * Based on:
4666  * Ext JS Library 1.1.1
4667  * Copyright(c) 2006-2007, Ext JS, LLC.
4668  *
4669  * Originally Released Under LGPL - original licence link has changed is not relivant.
4670  *
4671  * Fork - LGPL
4672  * <script type="text/javascript">
4673  */
4674
4675 // as we use this in bootstrap.
4676 Roo.namespace('Roo.form');
4677  /**
4678  * @class Roo.form.Action
4679  * Internal Class used to handle form actions
4680  * @constructor
4681  * @param {Roo.form.BasicForm} el The form element or its id
4682  * @param {Object} config Configuration options
4683  */
4684
4685  
4686  
4687 // define the action interface
4688 Roo.form.Action = function(form, options){
4689     this.form = form;
4690     this.options = options || {};
4691 };
4692 /**
4693  * Client Validation Failed
4694  * @const 
4695  */
4696 Roo.form.Action.CLIENT_INVALID = 'client';
4697 /**
4698  * Server Validation Failed
4699  * @const 
4700  */
4701 Roo.form.Action.SERVER_INVALID = 'server';
4702  /**
4703  * Connect to Server Failed
4704  * @const 
4705  */
4706 Roo.form.Action.CONNECT_FAILURE = 'connect';
4707 /**
4708  * Reading Data from Server Failed
4709  * @const 
4710  */
4711 Roo.form.Action.LOAD_FAILURE = 'load';
4712
4713 Roo.form.Action.prototype = {
4714     type : 'default',
4715     failureType : undefined,
4716     response : undefined,
4717     result : undefined,
4718
4719     // interface method
4720     run : function(options){
4721
4722     },
4723
4724     // interface method
4725     success : function(response){
4726
4727     },
4728
4729     // interface method
4730     handleResponse : function(response){
4731
4732     },
4733
4734     // default connection failure
4735     failure : function(response){
4736         
4737         this.response = response;
4738         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4739         this.form.afterAction(this, false);
4740     },
4741
4742     processResponse : function(response){
4743         this.response = response;
4744         if(!response.responseText){
4745             return true;
4746         }
4747         this.result = this.handleResponse(response);
4748         return this.result;
4749     },
4750
4751     // utility functions used internally
4752     getUrl : function(appendParams){
4753         var url = this.options.url || this.form.url || this.form.el.dom.action;
4754         if(appendParams){
4755             var p = this.getParams();
4756             if(p){
4757                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4758             }
4759         }
4760         return url;
4761     },
4762
4763     getMethod : function(){
4764         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4765     },
4766
4767     getParams : function(){
4768         var bp = this.form.baseParams;
4769         var p = this.options.params;
4770         if(p){
4771             if(typeof p == "object"){
4772                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4773             }else if(typeof p == 'string' && bp){
4774                 p += '&' + Roo.urlEncode(bp);
4775             }
4776         }else if(bp){
4777             p = Roo.urlEncode(bp);
4778         }
4779         return p;
4780     },
4781
4782     createCallback : function(){
4783         return {
4784             success: this.success,
4785             failure: this.failure,
4786             scope: this,
4787             timeout: (this.form.timeout*1000),
4788             upload: this.form.fileUpload ? this.success : undefined
4789         };
4790     }
4791 };
4792
4793 Roo.form.Action.Submit = function(form, options){
4794     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4795 };
4796
4797 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4798     type : 'submit',
4799
4800     haveProgress : false,
4801     uploadComplete : false,
4802     
4803     // uploadProgress indicator.
4804     uploadProgress : function()
4805     {
4806         if (!this.form.progressUrl) {
4807             return;
4808         }
4809         
4810         if (!this.haveProgress) {
4811             Roo.MessageBox.progress("Uploading", "Uploading");
4812         }
4813         if (this.uploadComplete) {
4814            Roo.MessageBox.hide();
4815            return;
4816         }
4817         
4818         this.haveProgress = true;
4819    
4820         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4821         
4822         var c = new Roo.data.Connection();
4823         c.request({
4824             url : this.form.progressUrl,
4825             params: {
4826                 id : uid
4827             },
4828             method: 'GET',
4829             success : function(req){
4830                //console.log(data);
4831                 var rdata = false;
4832                 var edata;
4833                 try  {
4834                    rdata = Roo.decode(req.responseText)
4835                 } catch (e) {
4836                     Roo.log("Invalid data from server..");
4837                     Roo.log(edata);
4838                     return;
4839                 }
4840                 if (!rdata || !rdata.success) {
4841                     Roo.log(rdata);
4842                     Roo.MessageBox.alert(Roo.encode(rdata));
4843                     return;
4844                 }
4845                 var data = rdata.data;
4846                 
4847                 if (this.uploadComplete) {
4848                    Roo.MessageBox.hide();
4849                    return;
4850                 }
4851                    
4852                 if (data){
4853                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4854                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4855                     );
4856                 }
4857                 this.uploadProgress.defer(2000,this);
4858             },
4859        
4860             failure: function(data) {
4861                 Roo.log('progress url failed ');
4862                 Roo.log(data);
4863             },
4864             scope : this
4865         });
4866            
4867     },
4868     
4869     
4870     run : function()
4871     {
4872         // run get Values on the form, so it syncs any secondary forms.
4873         this.form.getValues();
4874         
4875         var o = this.options;
4876         var method = this.getMethod();
4877         var isPost = method == 'POST';
4878         if(o.clientValidation === false || this.form.isValid()){
4879             
4880             if (this.form.progressUrl) {
4881                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4882                     (new Date() * 1) + '' + Math.random());
4883                     
4884             } 
4885             
4886             
4887             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4888                 form:this.form.el.dom,
4889                 url:this.getUrl(!isPost),
4890                 method: method,
4891                 params:isPost ? this.getParams() : null,
4892                 isUpload: this.form.fileUpload
4893             }));
4894             
4895             this.uploadProgress();
4896
4897         }else if (o.clientValidation !== false){ // client validation failed
4898             this.failureType = Roo.form.Action.CLIENT_INVALID;
4899             this.form.afterAction(this, false);
4900         }
4901     },
4902
4903     success : function(response)
4904     {
4905         this.uploadComplete= true;
4906         if (this.haveProgress) {
4907             Roo.MessageBox.hide();
4908         }
4909         
4910         
4911         var result = this.processResponse(response);
4912         if(result === true || result.success){
4913             this.form.afterAction(this, true);
4914             return;
4915         }
4916         if(result.errors){
4917             this.form.markInvalid(result.errors);
4918             this.failureType = Roo.form.Action.SERVER_INVALID;
4919         }
4920         this.form.afterAction(this, false);
4921     },
4922     failure : function(response)
4923     {
4924         this.uploadComplete= true;
4925         if (this.haveProgress) {
4926             Roo.MessageBox.hide();
4927         }
4928         
4929         this.response = response;
4930         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4931         this.form.afterAction(this, false);
4932     },
4933     
4934     handleResponse : function(response){
4935         if(this.form.errorReader){
4936             var rs = this.form.errorReader.read(response);
4937             var errors = [];
4938             if(rs.records){
4939                 for(var i = 0, len = rs.records.length; i < len; i++) {
4940                     var r = rs.records[i];
4941                     errors[i] = r.data;
4942                 }
4943             }
4944             if(errors.length < 1){
4945                 errors = null;
4946             }
4947             return {
4948                 success : rs.success,
4949                 errors : errors
4950             };
4951         }
4952         var ret = false;
4953         try {
4954             ret = Roo.decode(response.responseText);
4955         } catch (e) {
4956             ret = {
4957                 success: false,
4958                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4959                 errors : []
4960             };
4961         }
4962         return ret;
4963         
4964     }
4965 });
4966
4967
4968 Roo.form.Action.Load = function(form, options){
4969     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4970     this.reader = this.form.reader;
4971 };
4972
4973 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4974     type : 'load',
4975
4976     run : function(){
4977         
4978         Roo.Ajax.request(Roo.apply(
4979                 this.createCallback(), {
4980                     method:this.getMethod(),
4981                     url:this.getUrl(false),
4982                     params:this.getParams()
4983         }));
4984     },
4985
4986     success : function(response){
4987         
4988         var result = this.processResponse(response);
4989         if(result === true || !result.success || !result.data){
4990             this.failureType = Roo.form.Action.LOAD_FAILURE;
4991             this.form.afterAction(this, false);
4992             return;
4993         }
4994         this.form.clearInvalid();
4995         this.form.setValues(result.data);
4996         this.form.afterAction(this, true);
4997     },
4998
4999     handleResponse : function(response){
5000         if(this.form.reader){
5001             var rs = this.form.reader.read(response);
5002             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5003             return {
5004                 success : rs.success,
5005                 data : data
5006             };
5007         }
5008         return Roo.decode(response.responseText);
5009     }
5010 });
5011
5012 Roo.form.Action.ACTION_TYPES = {
5013     'load' : Roo.form.Action.Load,
5014     'submit' : Roo.form.Action.Submit
5015 };/*
5016  * - LGPL
5017  *
5018  * form
5019  * 
5020  */
5021
5022 /**
5023  * @class Roo.bootstrap.Form
5024  * @extends Roo.bootstrap.Component
5025  * Bootstrap Form class
5026  * @cfg {String} method  GET | POST (default POST)
5027  * @cfg {String} labelAlign top | left (default top)
5028   * @cfg {String} align left  | right - for navbars
5029
5030  * 
5031  * @constructor
5032  * Create a new Form
5033  * @param {Object} config The config object
5034  */
5035
5036
5037 Roo.bootstrap.Form = function(config){
5038     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5039     this.addEvents({
5040         /**
5041          * @event clientvalidation
5042          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5043          * @param {Form} this
5044          * @param {Boolean} valid true if the form has passed client-side validation
5045          */
5046         clientvalidation: true,
5047         /**
5048          * @event beforeaction
5049          * Fires before any action is performed. Return false to cancel the action.
5050          * @param {Form} this
5051          * @param {Action} action The action to be performed
5052          */
5053         beforeaction: true,
5054         /**
5055          * @event actionfailed
5056          * Fires when an action fails.
5057          * @param {Form} this
5058          * @param {Action} action The action that failed
5059          */
5060         actionfailed : true,
5061         /**
5062          * @event actioncomplete
5063          * Fires when an action is completed.
5064          * @param {Form} this
5065          * @param {Action} action The action that completed
5066          */
5067         actioncomplete : true
5068     });
5069     
5070 };
5071
5072 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5073       
5074      /**
5075      * @cfg {String} method
5076      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5077      */
5078     method : 'POST',
5079     /**
5080      * @cfg {String} url
5081      * The URL to use for form actions if one isn't supplied in the action options.
5082      */
5083     /**
5084      * @cfg {Boolean} fileUpload
5085      * Set to true if this form is a file upload.
5086      */
5087      
5088     /**
5089      * @cfg {Object} baseParams
5090      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5091      */
5092       
5093     /**
5094      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5095      */
5096     timeout: 30,
5097     /**
5098      * @cfg {Sting} align (left|right) for navbar forms
5099      */
5100     align : 'left',
5101
5102     // private
5103     activeAction : null,
5104  
5105     /**
5106      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5107      * element by passing it or its id or mask the form itself by passing in true.
5108      * @type Mixed
5109      */
5110     waitMsgTarget : false,
5111     
5112      
5113     
5114     /**
5115      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5116      * element by passing it or its id or mask the form itself by passing in true.
5117      * @type Mixed
5118      */
5119     
5120     getAutoCreate : function(){
5121         
5122         var cfg = {
5123             tag: 'form',
5124             method : this.method || 'POST',
5125             id : this.id || Roo.id(),
5126             cls : ''
5127         }
5128         if (this.parent().xtype.match(/^Nav/)) {
5129             cfg.cls = 'navbar-form navbar-' + this.align;
5130             
5131         }
5132         
5133         if (this.labelAlign == 'left' ) {
5134             cfg.cls += ' form-horizontal';
5135         }
5136         
5137         
5138         return cfg;
5139     },
5140     initEvents : function()
5141     {
5142         this.el.on('submit', this.onSubmit, this);
5143         
5144         
5145     },
5146     // private
5147     onSubmit : function(e){
5148         e.stopEvent();
5149     },
5150     
5151      /**
5152      * Returns true if client-side validation on the form is successful.
5153      * @return Boolean
5154      */
5155     isValid : function(){
5156         var items = this.getItems();
5157         var valid = true;
5158         items.each(function(f){
5159            if(!f.validate()){
5160                valid = false;
5161                
5162            }
5163         });
5164         return valid;
5165     },
5166     /**
5167      * Returns true if any fields in this form have changed since their original load.
5168      * @return Boolean
5169      */
5170     isDirty : function(){
5171         var dirty = false;
5172         var items = this.getItems();
5173         items.each(function(f){
5174            if(f.isDirty()){
5175                dirty = true;
5176                return false;
5177            }
5178            return true;
5179         });
5180         return dirty;
5181     },
5182      /**
5183      * Performs a predefined action (submit or load) or custom actions you define on this form.
5184      * @param {String} actionName The name of the action type
5185      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5186      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5187      * accept other config options):
5188      * <pre>
5189 Property          Type             Description
5190 ----------------  ---------------  ----------------------------------------------------------------------------------
5191 url               String           The url for the action (defaults to the form's url)
5192 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5193 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5194 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5195                                    validate the form on the client (defaults to false)
5196      * </pre>
5197      * @return {BasicForm} this
5198      */
5199     doAction : function(action, options){
5200         if(typeof action == 'string'){
5201             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5202         }
5203         if(this.fireEvent('beforeaction', this, action) !== false){
5204             this.beforeAction(action);
5205             action.run.defer(100, action);
5206         }
5207         return this;
5208     },
5209     
5210     // private
5211     beforeAction : function(action){
5212         var o = action.options;
5213         
5214         // not really supported yet.. ??
5215         
5216         //if(this.waitMsgTarget === true){
5217             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5218         //}else if(this.waitMsgTarget){
5219         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5220         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5221         //}else {
5222         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5223        // }
5224          
5225     },
5226
5227     // private
5228     afterAction : function(action, success){
5229         this.activeAction = null;
5230         var o = action.options;
5231         
5232         //if(this.waitMsgTarget === true){
5233             this.el.unmask();
5234         //}else if(this.waitMsgTarget){
5235         //    this.waitMsgTarget.unmask();
5236         //}else{
5237         //    Roo.MessageBox.updateProgress(1);
5238         //    Roo.MessageBox.hide();
5239        // }
5240         // 
5241         if(success){
5242             if(o.reset){
5243                 this.reset();
5244             }
5245             Roo.callback(o.success, o.scope, [this, action]);
5246             this.fireEvent('actioncomplete', this, action);
5247             
5248         }else{
5249             
5250             // failure condition..
5251             // we have a scenario where updates need confirming.
5252             // eg. if a locking scenario exists..
5253             // we look for { errors : { needs_confirm : true }} in the response.
5254             if (
5255                 (typeof(action.result) != 'undefined')  &&
5256                 (typeof(action.result.errors) != 'undefined')  &&
5257                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5258            ){
5259                 var _t = this;
5260                 Roo.log("not supported yet");
5261                  /*
5262                 
5263                 Roo.MessageBox.confirm(
5264                     "Change requires confirmation",
5265                     action.result.errorMsg,
5266                     function(r) {
5267                         if (r != 'yes') {
5268                             return;
5269                         }
5270                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5271                     }
5272                     
5273                 );
5274                 */
5275                 
5276                 
5277                 return;
5278             }
5279             
5280             Roo.callback(o.failure, o.scope, [this, action]);
5281             // show an error message if no failed handler is set..
5282             if (!this.hasListener('actionfailed')) {
5283                 Roo.log("need to add dialog support");
5284                 /*
5285                 Roo.MessageBox.alert("Error",
5286                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5287                         action.result.errorMsg :
5288                         "Saving Failed, please check your entries or try again"
5289                 );
5290                 */
5291             }
5292             
5293             this.fireEvent('actionfailed', this, action);
5294         }
5295         
5296     },
5297     /**
5298      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5299      * @param {String} id The value to search for
5300      * @return Field
5301      */
5302     findField : function(id){
5303         var items = this.getItems();
5304         var field = items.get(id);
5305         if(!field){
5306              items.each(function(f){
5307                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5308                     field = f;
5309                     return false;
5310                 }
5311                 return true;
5312             });
5313         }
5314         return field || null;
5315     },
5316      /**
5317      * Mark fields in this form invalid in bulk.
5318      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5319      * @return {BasicForm} this
5320      */
5321     markInvalid : function(errors){
5322         if(errors instanceof Array){
5323             for(var i = 0, len = errors.length; i < len; i++){
5324                 var fieldError = errors[i];
5325                 var f = this.findField(fieldError.id);
5326                 if(f){
5327                     f.markInvalid(fieldError.msg);
5328                 }
5329             }
5330         }else{
5331             var field, id;
5332             for(id in errors){
5333                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5334                     field.markInvalid(errors[id]);
5335                 }
5336             }
5337         }
5338         //Roo.each(this.childForms || [], function (f) {
5339         //    f.markInvalid(errors);
5340         //});
5341         
5342         return this;
5343     },
5344
5345     /**
5346      * Set values for fields in this form in bulk.
5347      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5348      * @return {BasicForm} this
5349      */
5350     setValues : function(values){
5351         if(values instanceof Array){ // array of objects
5352             for(var i = 0, len = values.length; i < len; i++){
5353                 var v = values[i];
5354                 var f = this.findField(v.id);
5355                 if(f){
5356                     f.setValue(v.value);
5357                     if(this.trackResetOnLoad){
5358                         f.originalValue = f.getValue();
5359                     }
5360                 }
5361             }
5362         }else{ // object hash
5363             var field, id;
5364             for(id in values){
5365                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5366                     
5367                     if (field.setFromData && 
5368                         field.valueField && 
5369                         field.displayField &&
5370                         // combos' with local stores can 
5371                         // be queried via setValue()
5372                         // to set their value..
5373                         (field.store && !field.store.isLocal)
5374                         ) {
5375                         // it's a combo
5376                         var sd = { };
5377                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5378                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5379                         field.setFromData(sd);
5380                         
5381                     } else {
5382                         field.setValue(values[id]);
5383                     }
5384                     
5385                     
5386                     if(this.trackResetOnLoad){
5387                         field.originalValue = field.getValue();
5388                     }
5389                 }
5390             }
5391         }
5392          
5393         //Roo.each(this.childForms || [], function (f) {
5394         //    f.setValues(values);
5395         //});
5396                 
5397         return this;
5398     },
5399
5400     /**
5401      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5402      * they are returned as an array.
5403      * @param {Boolean} asString
5404      * @return {Object}
5405      */
5406     getValues : function(asString){
5407         //if (this.childForms) {
5408             // copy values from the child forms
5409         //    Roo.each(this.childForms, function (f) {
5410         //        this.setValues(f.getValues());
5411         //    }, this);
5412         //}
5413         
5414         
5415         
5416         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5417         if(asString === true){
5418             return fs;
5419         }
5420         return Roo.urlDecode(fs);
5421     },
5422     
5423     /**
5424      * Returns the fields in this form as an object with key/value pairs. 
5425      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5426      * @return {Object}
5427      */
5428     getFieldValues : function(with_hidden)
5429     {
5430         var items = this.getItems();
5431         var ret = {};
5432         items.each(function(f){
5433             if (!f.getName()) {
5434                 return;
5435             }
5436             var v = f.getValue();
5437             if (f.inputType =='radio') {
5438                 if (typeof(ret[f.getName()]) == 'undefined') {
5439                     ret[f.getName()] = ''; // empty..
5440                 }
5441                 
5442                 if (!f.el.dom.checked) {
5443                     return;
5444                     
5445                 }
5446                 v = f.el.dom.value;
5447                 
5448             }
5449             
5450             // not sure if this supported any more..
5451             if ((typeof(v) == 'object') && f.getRawValue) {
5452                 v = f.getRawValue() ; // dates..
5453             }
5454             // combo boxes where name != hiddenName...
5455             if (f.name != f.getName()) {
5456                 ret[f.name] = f.getRawValue();
5457             }
5458             ret[f.getName()] = v;
5459         });
5460         
5461         return ret;
5462     },
5463
5464     /**
5465      * Clears all invalid messages in this form.
5466      * @return {BasicForm} this
5467      */
5468     clearInvalid : function(){
5469         var items = this.getItems();
5470         
5471         items.each(function(f){
5472            f.clearInvalid();
5473         });
5474         
5475         
5476         
5477         return this;
5478     },
5479
5480     /**
5481      * Resets this form.
5482      * @return {BasicForm} this
5483      */
5484     reset : function(){
5485         var items = this.getItems();
5486         items.each(function(f){
5487             f.reset();
5488         });
5489         
5490         Roo.each(this.childForms || [], function (f) {
5491             f.reset();
5492         });
5493        
5494         
5495         return this;
5496     },
5497     getItems : function()
5498     {
5499         var r=new Roo.util.MixedCollection(false, function(o){
5500             return o.id || (o.id = Roo.id());
5501         });
5502         var iter = function(el) {
5503             if (el.inputEl) {
5504                 r.add(el);
5505             }
5506             if (!el.items) {
5507                 return;
5508             }
5509             Roo.each(el.items,function(e) {
5510                 iter(e);
5511             });
5512             
5513             
5514         };
5515         iter(this);
5516         return r;
5517         
5518         
5519         
5520         
5521     }
5522     
5523 });
5524
5525  
5526 /*
5527  * Based on:
5528  * Ext JS Library 1.1.1
5529  * Copyright(c) 2006-2007, Ext JS, LLC.
5530  *
5531  * Originally Released Under LGPL - original licence link has changed is not relivant.
5532  *
5533  * Fork - LGPL
5534  * <script type="text/javascript">
5535  */
5536 /**
5537  * @class Roo.form.VTypes
5538  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5539  * @singleton
5540  */
5541 Roo.form.VTypes = function(){
5542     // closure these in so they are only created once.
5543     var alpha = /^[a-zA-Z_]+$/;
5544     var alphanum = /^[a-zA-Z0-9_]+$/;
5545     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5546     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5547
5548     // All these messages and functions are configurable
5549     return {
5550         /**
5551          * The function used to validate email addresses
5552          * @param {String} value The email address
5553          */
5554         'email' : function(v){
5555             return email.test(v);
5556         },
5557         /**
5558          * The error text to display when the email validation function returns false
5559          * @type String
5560          */
5561         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5562         /**
5563          * The keystroke filter mask to be applied on email input
5564          * @type RegExp
5565          */
5566         'emailMask' : /[a-z0-9_\.\-@]/i,
5567
5568         /**
5569          * The function used to validate URLs
5570          * @param {String} value The URL
5571          */
5572         'url' : function(v){
5573             return url.test(v);
5574         },
5575         /**
5576          * The error text to display when the url validation function returns false
5577          * @type String
5578          */
5579         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5580         
5581         /**
5582          * The function used to validate alpha values
5583          * @param {String} value The value
5584          */
5585         'alpha' : function(v){
5586             return alpha.test(v);
5587         },
5588         /**
5589          * The error text to display when the alpha validation function returns false
5590          * @type String
5591          */
5592         'alphaText' : 'This field should only contain letters and _',
5593         /**
5594          * The keystroke filter mask to be applied on alpha input
5595          * @type RegExp
5596          */
5597         'alphaMask' : /[a-z_]/i,
5598
5599         /**
5600          * The function used to validate alphanumeric values
5601          * @param {String} value The value
5602          */
5603         'alphanum' : function(v){
5604             return alphanum.test(v);
5605         },
5606         /**
5607          * The error text to display when the alphanumeric validation function returns false
5608          * @type String
5609          */
5610         'alphanumText' : 'This field should only contain letters, numbers and _',
5611         /**
5612          * The keystroke filter mask to be applied on alphanumeric input
5613          * @type RegExp
5614          */
5615         'alphanumMask' : /[a-z0-9_]/i
5616     };
5617 }();/*
5618  * - LGPL
5619  *
5620  * Input
5621  * 
5622  */
5623
5624 /**
5625  * @class Roo.bootstrap.Input
5626  * @extends Roo.bootstrap.Component
5627  * Bootstrap Input class
5628  * @cfg {Boolean} disabled is it disabled
5629  * @cfg {String} fieldLabel - the label associated
5630  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5631  * @cfg {String} name name of the input
5632  * @cfg {string} fieldLabel - the label associated
5633  * @cfg {string}  inputType - input / file submit ...
5634  * @cfg {string} placeholder - placeholder to put in text.
5635  * @cfg {string}  before - input group add on before
5636  * @cfg {string} after - input group add on after
5637  * @cfg {string} size - (lg|sm) or leave empty..
5638  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5639  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5640  * @cfg {Number} md colspan out of 12 for computer-sized screens
5641  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5642  * @cfg {string} value default value of the input
5643  * @cfg {Number} labelWidth set the width of label (0-12)
5644  * @cfg {String} labelAlign (top|left)
5645  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5646  * 
5647  * 
5648  * @constructor
5649  * Create a new Input
5650  * @param {Object} config The config object
5651  */
5652
5653 Roo.bootstrap.Input = function(config){
5654     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5655    
5656         this.addEvents({
5657             /**
5658              * @event focus
5659              * Fires when this field receives input focus.
5660              * @param {Roo.form.Field} this
5661              */
5662             focus : true,
5663             /**
5664              * @event blur
5665              * Fires when this field loses input focus.
5666              * @param {Roo.form.Field} this
5667              */
5668             blur : true,
5669             /**
5670              * @event specialkey
5671              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5672              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5673              * @param {Roo.form.Field} this
5674              * @param {Roo.EventObject} e The event object
5675              */
5676             specialkey : true,
5677             /**
5678              * @event change
5679              * Fires just before the field blurs if the field value has changed.
5680              * @param {Roo.form.Field} this
5681              * @param {Mixed} newValue The new value
5682              * @param {Mixed} oldValue The original value
5683              */
5684             change : true,
5685             /**
5686              * @event invalid
5687              * Fires after the field has been marked as invalid.
5688              * @param {Roo.form.Field} this
5689              * @param {String} msg The validation message
5690              */
5691             invalid : true,
5692             /**
5693              * @event valid
5694              * Fires after the field has been validated with no errors.
5695              * @param {Roo.form.Field} this
5696              */
5697             valid : true,
5698              /**
5699              * @event keyup
5700              * Fires after the key up
5701              * @param {Roo.form.Field} this
5702              * @param {Roo.EventObject}  e The event Object
5703              */
5704             keyup : true
5705         });
5706 };
5707
5708 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5709      /**
5710      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5711       automatic validation (defaults to "keyup").
5712      */
5713     validationEvent : "keyup",
5714      /**
5715      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5716      */
5717     validateOnBlur : true,
5718     /**
5719      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5720      */
5721     validationDelay : 250,
5722      /**
5723      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5724      */
5725     focusClass : "x-form-focus",  // not needed???
5726     
5727        
5728     /**
5729      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5730      */
5731     invalidClass : "has-error",
5732     
5733     /**
5734      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5735      */
5736     selectOnFocus : false,
5737     
5738      /**
5739      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5740      */
5741     maskRe : null,
5742        /**
5743      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5744      */
5745     vtype : null,
5746     
5747       /**
5748      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5749      */
5750     disableKeyFilter : false,
5751     
5752        /**
5753      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5754      */
5755     disabled : false,
5756      /**
5757      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5758      */
5759     allowBlank : true,
5760     /**
5761      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5762      */
5763     blankText : "This field is required",
5764     
5765      /**
5766      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5767      */
5768     minLength : 0,
5769     /**
5770      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5771      */
5772     maxLength : Number.MAX_VALUE,
5773     /**
5774      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5775      */
5776     minLengthText : "The minimum length for this field is {0}",
5777     /**
5778      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5779      */
5780     maxLengthText : "The maximum length for this field is {0}",
5781   
5782     
5783     /**
5784      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5785      * If available, this function will be called only after the basic validators all return true, and will be passed the
5786      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5787      */
5788     validator : null,
5789     /**
5790      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5791      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5792      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5793      */
5794     regex : null,
5795     /**
5796      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5797      */
5798     regexText : "",
5799     
5800     
5801     
5802     fieldLabel : '',
5803     inputType : 'text',
5804     
5805     name : false,
5806     placeholder: false,
5807     before : false,
5808     after : false,
5809     size : false,
5810     // private
5811     hasFocus : false,
5812     preventMark: false,
5813     isFormField : true,
5814     value : '',
5815     labelWidth : 2,
5816     labelAlign : false,
5817     readOnly : false,
5818     
5819     parentLabelAlign : function()
5820     {
5821         var parent = this;
5822         while (parent.parent()) {
5823             parent = parent.parent();
5824             if (typeof(parent.labelAlign) !='undefined') {
5825                 return parent.labelAlign;
5826             }
5827         }
5828         return 'left';
5829         
5830     },
5831     
5832     getAutoCreate : function(){
5833         
5834         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5835         
5836         var id = Roo.id();
5837         
5838         var cfg = {};
5839         
5840         if(this.inputType != 'hidden'){
5841             cfg.cls = 'form-group' //input-group
5842         }
5843         
5844         var input =  {
5845             tag: 'input',
5846             id : id,
5847             type : this.inputType,
5848             value : this.value,
5849             cls : 'form-control',
5850             placeholder : this.placeholder || ''
5851             
5852         };
5853         
5854         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5855             input.maxLength = this.maxLength;
5856         }
5857         
5858         if (this.disabled) {
5859             input.disabled=true;
5860         }
5861         
5862         if (this.readOnly) {
5863             input.readonly=true;
5864         }
5865         
5866         if (this.name) {
5867             input.name = this.name;
5868         }
5869         if (this.size) {
5870             input.cls += ' input-' + this.size;
5871         }
5872         var settings=this;
5873         ['xs','sm','md','lg'].map(function(size){
5874             if (settings[size]) {
5875                 cfg.cls += ' col-' + size + '-' + settings[size];
5876             }
5877         });
5878         
5879         var inputblock = input;
5880         
5881         if (this.before || this.after) {
5882             
5883             inputblock = {
5884                 cls : 'input-group',
5885                 cn :  [] 
5886             };
5887             if (this.before) {
5888                 inputblock.cn.push({
5889                     tag :'span',
5890                     cls : 'input-group-addon',
5891                     html : this.before
5892                 });
5893             }
5894             inputblock.cn.push(input);
5895             if (this.after) {
5896                 inputblock.cn.push({
5897                     tag :'span',
5898                     cls : 'input-group-addon',
5899                     html : this.after
5900                 });
5901             }
5902             
5903         };
5904         
5905         if (align ==='left' && this.fieldLabel.length) {
5906                 Roo.log("left and has label");
5907                 cfg.cn = [
5908                     
5909                     {
5910                         tag: 'label',
5911                         'for' :  id,
5912                         cls : 'control-label col-sm-' + this.labelWidth,
5913                         html : this.fieldLabel
5914                         
5915                     },
5916                     {
5917                         cls : "col-sm-" + (12 - this.labelWidth), 
5918                         cn: [
5919                             inputblock
5920                         ]
5921                     }
5922                     
5923                 ];
5924         } else if ( this.fieldLabel.length) {
5925                 Roo.log(" label");
5926                  cfg.cn = [
5927                    
5928                     {
5929                         tag: 'label',
5930                         //cls : 'input-group-addon',
5931                         html : this.fieldLabel
5932                         
5933                     },
5934                     
5935                     inputblock
5936                     
5937                 ];
5938
5939         } else {
5940             
5941                 Roo.log(" no label && no align");
5942                 cfg.cn = [
5943                     
5944                         inputblock
5945                     
5946                 ];
5947                 
5948                 
5949         };
5950         Roo.log('input-parentType: ' + this.parentType);
5951         
5952         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5953            cfg.cls += ' navbar-form';
5954            Roo.log(cfg);
5955         }
5956         
5957         return cfg;
5958         
5959     },
5960     /**
5961      * return the real input element.
5962      */
5963     inputEl: function ()
5964     {
5965         return this.el.select('input.form-control',true).first();
5966     },
5967     setDisabled : function(v)
5968     {
5969         var i  = this.inputEl().dom;
5970         if (!v) {
5971             i.removeAttribute('disabled');
5972             return;
5973             
5974         }
5975         i.setAttribute('disabled','true');
5976     },
5977     initEvents : function()
5978     {
5979         
5980         this.inputEl().on("keydown" , this.fireKey,  this);
5981         this.inputEl().on("focus", this.onFocus,  this);
5982         this.inputEl().on("blur", this.onBlur,  this);
5983         
5984         this.inputEl().relayEvent('keyup', this);
5985
5986         // reference to original value for reset
5987         this.originalValue = this.getValue();
5988         //Roo.form.TextField.superclass.initEvents.call(this);
5989         if(this.validationEvent == 'keyup'){
5990             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5991             this.inputEl().on('keyup', this.filterValidation, this);
5992         }
5993         else if(this.validationEvent !== false){
5994             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5995         }
5996         
5997         if(this.selectOnFocus){
5998             this.on("focus", this.preFocus, this);
5999             
6000         }
6001         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6002             this.inputEl().on("keypress", this.filterKeys, this);
6003         }
6004        /* if(this.grow){
6005             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6006             this.el.on("click", this.autoSize,  this);
6007         }
6008         */
6009         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6010             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6011         }
6012         
6013     },
6014     filterValidation : function(e){
6015         if(!e.isNavKeyPress()){
6016             this.validationTask.delay(this.validationDelay);
6017         }
6018     },
6019      /**
6020      * Validates the field value
6021      * @return {Boolean} True if the value is valid, else false
6022      */
6023     validate : function(){
6024         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6025         if(this.disabled || this.validateValue(this.getRawValue())){
6026             this.clearInvalid();
6027             return true;
6028         }
6029         return false;
6030     },
6031     
6032     
6033     /**
6034      * Validates a value according to the field's validation rules and marks the field as invalid
6035      * if the validation fails
6036      * @param {Mixed} value The value to validate
6037      * @return {Boolean} True if the value is valid, else false
6038      */
6039     validateValue : function(value){
6040         if(value.length < 1)  { // if it's blank
6041              if(this.allowBlank){
6042                 this.clearInvalid();
6043                 return true;
6044              }else{
6045                 this.markInvalid(this.blankText);
6046                 return false;
6047              }
6048         }
6049         if(value.length < this.minLength){
6050             this.markInvalid(String.format(this.minLengthText, this.minLength));
6051             return false;
6052         }
6053         if(value.length > this.maxLength){
6054             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6055             return false;
6056         }
6057         if(this.vtype){
6058             var vt = Roo.form.VTypes;
6059             if(!vt[this.vtype](value, this)){
6060                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6061                 return false;
6062             }
6063         }
6064         if(typeof this.validator == "function"){
6065             var msg = this.validator(value);
6066             if(msg !== true){
6067                 this.markInvalid(msg);
6068                 return false;
6069             }
6070         }
6071         if(this.regex && !this.regex.test(value)){
6072             this.markInvalid(this.regexText);
6073             return false;
6074         }
6075         return true;
6076     },
6077
6078     
6079     
6080      // private
6081     fireKey : function(e){
6082         //Roo.log('field ' + e.getKey());
6083         if(e.isNavKeyPress()){
6084             this.fireEvent("specialkey", this, e);
6085         }
6086     },
6087     focus : function (selectText){
6088         if(this.rendered){
6089             this.inputEl().focus();
6090             if(selectText === true){
6091                 this.inputEl().dom.select();
6092             }
6093         }
6094         return this;
6095     } ,
6096     
6097     onFocus : function(){
6098         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6099            // this.el.addClass(this.focusClass);
6100         }
6101         if(!this.hasFocus){
6102             this.hasFocus = true;
6103             this.startValue = this.getValue();
6104             this.fireEvent("focus", this);
6105         }
6106     },
6107     
6108     beforeBlur : Roo.emptyFn,
6109
6110     
6111     // private
6112     onBlur : function(){
6113         this.beforeBlur();
6114         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6115             //this.el.removeClass(this.focusClass);
6116         }
6117         this.hasFocus = false;
6118         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6119             this.validate();
6120         }
6121         var v = this.getValue();
6122         if(String(v) !== String(this.startValue)){
6123             this.fireEvent('change', this, v, this.startValue);
6124         }
6125         this.fireEvent("blur", this);
6126     },
6127     
6128     /**
6129      * Resets the current field value to the originally loaded value and clears any validation messages
6130      */
6131     reset : function(){
6132         this.setValue(this.originalValue);
6133         this.clearInvalid();
6134     },
6135      /**
6136      * Returns the name of the field
6137      * @return {Mixed} name The name field
6138      */
6139     getName: function(){
6140         return this.name;
6141     },
6142      /**
6143      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6144      * @return {Mixed} value The field value
6145      */
6146     getValue : function(){
6147         return this.inputEl().getValue();
6148     },
6149     /**
6150      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6151      * @return {Mixed} value The field value
6152      */
6153     getRawValue : function(){
6154         var v = this.inputEl().getValue();
6155         
6156         return v;
6157     },
6158     
6159     /**
6160      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6161      * @param {Mixed} value The value to set
6162      */
6163     setRawValue : function(v){
6164         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6165     },
6166     
6167     selectText : function(start, end){
6168         var v = this.getRawValue();
6169         if(v.length > 0){
6170             start = start === undefined ? 0 : start;
6171             end = end === undefined ? v.length : end;
6172             var d = this.inputEl().dom;
6173             if(d.setSelectionRange){
6174                 d.setSelectionRange(start, end);
6175             }else if(d.createTextRange){
6176                 var range = d.createTextRange();
6177                 range.moveStart("character", start);
6178                 range.moveEnd("character", v.length-end);
6179                 range.select();
6180             }
6181         }
6182     },
6183     
6184     /**
6185      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6186      * @param {Mixed} value The value to set
6187      */
6188     setValue : function(v){
6189         this.value = v;
6190         if(this.rendered){
6191             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6192             this.validate();
6193         }
6194     },
6195     
6196     /*
6197     processValue : function(value){
6198         if(this.stripCharsRe){
6199             var newValue = value.replace(this.stripCharsRe, '');
6200             if(newValue !== value){
6201                 this.setRawValue(newValue);
6202                 return newValue;
6203             }
6204         }
6205         return value;
6206     },
6207   */
6208     preFocus : function(){
6209         
6210         if(this.selectOnFocus){
6211             this.inputEl().dom.select();
6212         }
6213     },
6214     filterKeys : function(e){
6215         var k = e.getKey();
6216         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6217             return;
6218         }
6219         var c = e.getCharCode(), cc = String.fromCharCode(c);
6220         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6221             return;
6222         }
6223         if(!this.maskRe.test(cc)){
6224             e.stopEvent();
6225         }
6226     },
6227      /**
6228      * Clear any invalid styles/messages for this field
6229      */
6230     clearInvalid : function(){
6231         
6232         if(!this.el || this.preventMark){ // not rendered
6233             return;
6234         }
6235         this.el.removeClass(this.invalidClass);
6236         /*
6237         switch(this.msgTarget){
6238             case 'qtip':
6239                 this.el.dom.qtip = '';
6240                 break;
6241             case 'title':
6242                 this.el.dom.title = '';
6243                 break;
6244             case 'under':
6245                 if(this.errorEl){
6246                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6247                 }
6248                 break;
6249             case 'side':
6250                 if(this.errorIcon){
6251                     this.errorIcon.dom.qtip = '';
6252                     this.errorIcon.hide();
6253                     this.un('resize', this.alignErrorIcon, this);
6254                 }
6255                 break;
6256             default:
6257                 var t = Roo.getDom(this.msgTarget);
6258                 t.innerHTML = '';
6259                 t.style.display = 'none';
6260                 break;
6261         }
6262         */
6263         this.fireEvent('valid', this);
6264     },
6265      /**
6266      * Mark this field as invalid
6267      * @param {String} msg The validation message
6268      */
6269     markInvalid : function(msg){
6270         if(!this.el  || this.preventMark){ // not rendered
6271             return;
6272         }
6273         this.el.addClass(this.invalidClass);
6274         /*
6275         msg = msg || this.invalidText;
6276         switch(this.msgTarget){
6277             case 'qtip':
6278                 this.el.dom.qtip = msg;
6279                 this.el.dom.qclass = 'x-form-invalid-tip';
6280                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6281                     Roo.QuickTips.enable();
6282                 }
6283                 break;
6284             case 'title':
6285                 this.el.dom.title = msg;
6286                 break;
6287             case 'under':
6288                 if(!this.errorEl){
6289                     var elp = this.el.findParent('.x-form-element', 5, true);
6290                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6291                     this.errorEl.setWidth(elp.getWidth(true)-20);
6292                 }
6293                 this.errorEl.update(msg);
6294                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6295                 break;
6296             case 'side':
6297                 if(!this.errorIcon){
6298                     var elp = this.el.findParent('.x-form-element', 5, true);
6299                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6300                 }
6301                 this.alignErrorIcon();
6302                 this.errorIcon.dom.qtip = msg;
6303                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6304                 this.errorIcon.show();
6305                 this.on('resize', this.alignErrorIcon, this);
6306                 break;
6307             default:
6308                 var t = Roo.getDom(this.msgTarget);
6309                 t.innerHTML = msg;
6310                 t.style.display = this.msgDisplay;
6311                 break;
6312         }
6313         */
6314         this.fireEvent('invalid', this, msg);
6315     },
6316     // private
6317     SafariOnKeyDown : function(event)
6318     {
6319         // this is a workaround for a password hang bug on chrome/ webkit.
6320         
6321         var isSelectAll = false;
6322         
6323         if(this.inputEl().dom.selectionEnd > 0){
6324             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6325         }
6326         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6327             event.preventDefault();
6328             this.setValue('');
6329             return;
6330         }
6331         
6332         if(isSelectAll){ // backspace and delete key
6333             
6334             event.preventDefault();
6335             // this is very hacky as keydown always get's upper case.
6336             //
6337             var cc = String.fromCharCode(event.getCharCode());
6338             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6339             
6340         }
6341     },
6342     adjustWidth : function(tag, w){
6343         tag = tag.toLowerCase();
6344         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6345             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6346                 if(tag == 'input'){
6347                     return w + 2;
6348                 }
6349                 if(tag == 'textarea'){
6350                     return w-2;
6351                 }
6352             }else if(Roo.isOpera){
6353                 if(tag == 'input'){
6354                     return w + 2;
6355                 }
6356                 if(tag == 'textarea'){
6357                     return w-2;
6358                 }
6359             }
6360         }
6361         return w;
6362     }
6363     
6364 });
6365
6366  
6367 /*
6368  * - LGPL
6369  *
6370  * Input
6371  * 
6372  */
6373
6374 /**
6375  * @class Roo.bootstrap.TextArea
6376  * @extends Roo.bootstrap.Input
6377  * Bootstrap TextArea class
6378  * @cfg {Number} cols Specifies the visible width of a text area
6379  * @cfg {Number} rows Specifies the visible number of lines in a text area
6380  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6381  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6382  * @cfg {string} html text
6383  * 
6384  * @constructor
6385  * Create a new TextArea
6386  * @param {Object} config The config object
6387  */
6388
6389 Roo.bootstrap.TextArea = function(config){
6390     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6391    
6392 };
6393
6394 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6395      
6396     cols : false,
6397     rows : 5,
6398     readOnly : false,
6399     warp : 'soft',
6400     resize : false,
6401     value: false,
6402     html: false,
6403     
6404     getAutoCreate : function(){
6405         
6406         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6407         
6408         var id = Roo.id();
6409         
6410         var cfg = {};
6411         
6412         var input =  {
6413             tag: 'textarea',
6414             id : id,
6415             warp : this.warp,
6416             rows : this.rows,
6417             value : this.value || '',
6418             html: this.html || '',
6419             cls : 'form-control',
6420             placeholder : this.placeholder || '' 
6421             
6422         };
6423         
6424         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6425             input.maxLength = this.maxLength;
6426         }
6427         
6428         if(this.resize){
6429             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6430         }
6431         
6432         if(this.cols){
6433             input.cols = this.cols;
6434         }
6435         
6436         if (this.readOnly) {
6437             input.readonly = true;
6438         }
6439         
6440         if (this.name) {
6441             input.name = this.name;
6442         }
6443         
6444         if (this.size) {
6445             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6446         }
6447         
6448         var settings=this;
6449         ['xs','sm','md','lg'].map(function(size){
6450             if (settings[size]) {
6451                 cfg.cls += ' col-' + size + '-' + settings[size];
6452             }
6453         });
6454         
6455         var inputblock = input;
6456         
6457         if (this.before || this.after) {
6458             
6459             inputblock = {
6460                 cls : 'input-group',
6461                 cn :  [] 
6462             };
6463             if (this.before) {
6464                 inputblock.cn.push({
6465                     tag :'span',
6466                     cls : 'input-group-addon',
6467                     html : this.before
6468                 });
6469             }
6470             inputblock.cn.push(input);
6471             if (this.after) {
6472                 inputblock.cn.push({
6473                     tag :'span',
6474                     cls : 'input-group-addon',
6475                     html : this.after
6476                 });
6477             }
6478             
6479         }
6480         
6481         if (align ==='left' && this.fieldLabel.length) {
6482                 Roo.log("left and has label");
6483                 cfg.cn = [
6484                     
6485                     {
6486                         tag: 'label',
6487                         'for' :  id,
6488                         cls : 'control-label col-sm-' + this.labelWidth,
6489                         html : this.fieldLabel
6490                         
6491                     },
6492                     {
6493                         cls : "col-sm-" + (12 - this.labelWidth), 
6494                         cn: [
6495                             inputblock
6496                         ]
6497                     }
6498                     
6499                 ];
6500         } else if ( this.fieldLabel.length) {
6501                 Roo.log(" label");
6502                  cfg.cn = [
6503                    
6504                     {
6505                         tag: 'label',
6506                         //cls : 'input-group-addon',
6507                         html : this.fieldLabel
6508                         
6509                     },
6510                     
6511                     inputblock
6512                     
6513                 ];
6514
6515         } else {
6516             
6517                    Roo.log(" no label && no align");
6518                 cfg.cn = [
6519                     
6520                         inputblock
6521                     
6522                 ];
6523                 
6524                 
6525         }
6526         
6527         if (this.disabled) {
6528             input.disabled=true;
6529         }
6530         
6531         return cfg;
6532         
6533     },
6534     /**
6535      * return the real textarea element.
6536      */
6537     inputEl: function ()
6538     {
6539         return this.el.select('textarea.form-control',true).first();
6540     }
6541 });
6542
6543  
6544 /*
6545  * - LGPL
6546  *
6547  * trigger field - base class for combo..
6548  * 
6549  */
6550  
6551 /**
6552  * @class Roo.bootstrap.TriggerField
6553  * @extends Roo.bootstrap.Input
6554  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6555  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6556  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6557  * for which you can provide a custom implementation.  For example:
6558  * <pre><code>
6559 var trigger = new Roo.bootstrap.TriggerField();
6560 trigger.onTriggerClick = myTriggerFn;
6561 trigger.applyTo('my-field');
6562 </code></pre>
6563  *
6564  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6565  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6566  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6567  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6568  * @constructor
6569  * Create a new TriggerField.
6570  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6571  * to the base TextField)
6572  */
6573 Roo.bootstrap.TriggerField = function(config){
6574     this.mimicing = false;
6575     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6576 };
6577
6578 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6579     /**
6580      * @cfg {String} triggerClass A CSS class to apply to the trigger
6581      */
6582      /**
6583      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6584      */
6585     hideTrigger:false,
6586
6587     /** @cfg {Boolean} grow @hide */
6588     /** @cfg {Number} growMin @hide */
6589     /** @cfg {Number} growMax @hide */
6590
6591     /**
6592      * @hide 
6593      * @method
6594      */
6595     autoSize: Roo.emptyFn,
6596     // private
6597     monitorTab : true,
6598     // private
6599     deferHeight : true,
6600
6601     
6602     actionMode : 'wrap',
6603     
6604     
6605     
6606     getAutoCreate : function(){
6607        
6608         var parent = this.parent();
6609         
6610         var align = this.parentLabelAlign();
6611         
6612         var id = Roo.id();
6613         
6614         var cfg = {
6615             cls: 'form-group' //input-group
6616         };
6617         
6618         
6619         var input =  {
6620             tag: 'input',
6621             id : id,
6622             type : this.inputType,
6623             cls : 'form-control',
6624             autocomplete: 'off',
6625             placeholder : this.placeholder || '' 
6626             
6627         };
6628         if (this.name) {
6629             input.name = this.name;
6630         }
6631         if (this.size) {
6632             input.cls += ' input-' + this.size;
6633         }
6634         
6635         if (this.disabled) {
6636             input.disabled=true;
6637         }
6638         
6639         var inputblock = input;
6640         
6641         if (this.before || this.after) {
6642             
6643             inputblock = {
6644                 cls : 'input-group',
6645                 cn :  [] 
6646             };
6647             if (this.before) {
6648                 inputblock.cn.push({
6649                     tag :'span',
6650                     cls : 'input-group-addon',
6651                     html : this.before
6652                 });
6653             }
6654             inputblock.cn.push(input);
6655             if (this.after) {
6656                 inputblock.cn.push({
6657                     tag :'span',
6658                     cls : 'input-group-addon',
6659                     html : this.after
6660                 });
6661             }
6662             
6663         };
6664         
6665         var box = {
6666             tag: 'div',
6667             cn: [
6668                 {
6669                     tag: 'input',
6670                     type : 'hidden',
6671                     cls: 'form-hidden-field'
6672                 },
6673                 inputblock
6674             ]
6675             
6676         };
6677         
6678         if(this.multiple){
6679             Roo.log('multiple');
6680             
6681             box = {
6682                 tag: 'div',
6683                 cn: [
6684                     {
6685                         tag: 'input',
6686                         type : 'hidden',
6687                         cls: 'form-hidden-field'
6688                     },
6689                     {
6690                         tag: 'ul',
6691                         cls: 'select2-choices',
6692                         cn:[
6693                             {
6694                                 tag: 'li',
6695                                 cls: 'select2-search-field',
6696                                 cn: [
6697
6698                                     inputblock
6699                                 ]
6700                             }
6701                         ]
6702                     }
6703                 ]
6704             }
6705         };
6706         
6707         var combobox = {
6708             cls: 'select2-container input-group',
6709             cn: [
6710                 box,
6711                 {
6712                     tag: 'ul',
6713                     cls: 'typeahead typeahead-long dropdown-menu',
6714                     style: 'display:none'
6715                 }
6716             ]
6717         };
6718         
6719         if(!this.multiple){
6720             combobox.cn.push({
6721                 tag :'span',
6722                 cls : 'input-group-addon btn dropdown-toggle',
6723                 cn : [
6724                     {
6725                         tag: 'span',
6726                         cls: 'caret'
6727                     },
6728                     {
6729                         tag: 'span',
6730                         cls: 'combobox-clear',
6731                         cn  : [
6732                             {
6733                                 tag : 'i',
6734                                 cls: 'icon-remove'
6735                             }
6736                         ]
6737                     }
6738                 ]
6739
6740             })
6741         }
6742         
6743         if(this.multiple){
6744             combobox.cls += ' select2-container-multi';
6745         }
6746         
6747         if (align ==='left' && this.fieldLabel.length) {
6748             
6749                 Roo.log("left and has label");
6750                 cfg.cn = [
6751                     
6752                     {
6753                         tag: 'label',
6754                         'for' :  id,
6755                         cls : 'control-label col-sm-' + this.labelWidth,
6756                         html : this.fieldLabel
6757                         
6758                     },
6759                     {
6760                         cls : "col-sm-" + (12 - this.labelWidth), 
6761                         cn: [
6762                             combobox
6763                         ]
6764                     }
6765                     
6766                 ];
6767         } else if ( this.fieldLabel.length) {
6768                 Roo.log(" label");
6769                  cfg.cn = [
6770                    
6771                     {
6772                         tag: 'label',
6773                         //cls : 'input-group-addon',
6774                         html : this.fieldLabel
6775                         
6776                     },
6777                     
6778                     combobox
6779                     
6780                 ];
6781
6782         } else {
6783             
6784                 Roo.log(" no label && no align");
6785                 cfg = combobox
6786                      
6787                 
6788         }
6789          
6790         var settings=this;
6791         ['xs','sm','md','lg'].map(function(size){
6792             if (settings[size]) {
6793                 cfg.cls += ' col-' + size + '-' + settings[size];
6794             }
6795         });
6796         
6797         return cfg;
6798         
6799     },
6800     
6801     
6802     
6803     // private
6804     onResize : function(w, h){
6805 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6806 //        if(typeof w == 'number'){
6807 //            var x = w - this.trigger.getWidth();
6808 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6809 //            this.trigger.setStyle('left', x+'px');
6810 //        }
6811     },
6812
6813     // private
6814     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6815
6816     // private
6817     getResizeEl : function(){
6818         return this.inputEl();
6819     },
6820
6821     // private
6822     getPositionEl : function(){
6823         return this.inputEl();
6824     },
6825
6826     // private
6827     alignErrorIcon : function(){
6828         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6829     },
6830
6831     // private
6832     initEvents : function(){
6833         
6834         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6835         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6836         if(!this.multiple){
6837             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6838             if(this.hideTrigger){
6839                 this.trigger.setDisplayed(false);
6840             }
6841             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6842         }
6843         
6844         if(this.multiple){
6845             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6846         }
6847         
6848         //this.trigger.addClassOnOver('x-form-trigger-over');
6849         //this.trigger.addClassOnClick('x-form-trigger-click');
6850         
6851         //if(!this.width){
6852         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6853         //}
6854     },
6855
6856     // private
6857     initTrigger : function(){
6858        
6859     },
6860
6861     // private
6862     onDestroy : function(){
6863         if(this.trigger){
6864             this.trigger.removeAllListeners();
6865           //  this.trigger.remove();
6866         }
6867         //if(this.wrap){
6868         //    this.wrap.remove();
6869         //}
6870         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6871     },
6872
6873     // private
6874     onFocus : function(){
6875         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6876         /*
6877         if(!this.mimicing){
6878             this.wrap.addClass('x-trigger-wrap-focus');
6879             this.mimicing = true;
6880             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6881             if(this.monitorTab){
6882                 this.el.on("keydown", this.checkTab, this);
6883             }
6884         }
6885         */
6886     },
6887
6888     // private
6889     checkTab : function(e){
6890         if(e.getKey() == e.TAB){
6891             this.triggerBlur();
6892         }
6893     },
6894
6895     // private
6896     onBlur : function(){
6897         // do nothing
6898     },
6899
6900     // private
6901     mimicBlur : function(e, t){
6902         /*
6903         if(!this.wrap.contains(t) && this.validateBlur()){
6904             this.triggerBlur();
6905         }
6906         */
6907     },
6908
6909     // private
6910     triggerBlur : function(){
6911         this.mimicing = false;
6912         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6913         if(this.monitorTab){
6914             this.el.un("keydown", this.checkTab, this);
6915         }
6916         //this.wrap.removeClass('x-trigger-wrap-focus');
6917         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6918     },
6919
6920     // private
6921     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6922     validateBlur : function(e, t){
6923         return true;
6924     },
6925
6926     // private
6927     onDisable : function(){
6928         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6929         //if(this.wrap){
6930         //    this.wrap.addClass('x-item-disabled');
6931         //}
6932     },
6933
6934     // private
6935     onEnable : function(){
6936         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6937         //if(this.wrap){
6938         //    this.el.removeClass('x-item-disabled');
6939         //}
6940     },
6941
6942     // private
6943     onShow : function(){
6944         var ae = this.getActionEl();
6945         
6946         if(ae){
6947             ae.dom.style.display = '';
6948             ae.dom.style.visibility = 'visible';
6949         }
6950     },
6951
6952     // private
6953     
6954     onHide : function(){
6955         var ae = this.getActionEl();
6956         ae.dom.style.display = 'none';
6957     },
6958
6959     /**
6960      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6961      * by an implementing function.
6962      * @method
6963      * @param {EventObject} e
6964      */
6965     onTriggerClick : Roo.emptyFn
6966 });
6967  /*
6968  * Based on:
6969  * Ext JS Library 1.1.1
6970  * Copyright(c) 2006-2007, Ext JS, LLC.
6971  *
6972  * Originally Released Under LGPL - original licence link has changed is not relivant.
6973  *
6974  * Fork - LGPL
6975  * <script type="text/javascript">
6976  */
6977
6978
6979 /**
6980  * @class Roo.data.SortTypes
6981  * @singleton
6982  * Defines the default sorting (casting?) comparison functions used when sorting data.
6983  */
6984 Roo.data.SortTypes = {
6985     /**
6986      * Default sort that does nothing
6987      * @param {Mixed} s The value being converted
6988      * @return {Mixed} The comparison value
6989      */
6990     none : function(s){
6991         return s;
6992     },
6993     
6994     /**
6995      * The regular expression used to strip tags
6996      * @type {RegExp}
6997      * @property
6998      */
6999     stripTagsRE : /<\/?[^>]+>/gi,
7000     
7001     /**
7002      * Strips all HTML tags to sort on text only
7003      * @param {Mixed} s The value being converted
7004      * @return {String} The comparison value
7005      */
7006     asText : function(s){
7007         return String(s).replace(this.stripTagsRE, "");
7008     },
7009     
7010     /**
7011      * Strips all HTML tags to sort on text only - Case insensitive
7012      * @param {Mixed} s The value being converted
7013      * @return {String} The comparison value
7014      */
7015     asUCText : function(s){
7016         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7017     },
7018     
7019     /**
7020      * Case insensitive string
7021      * @param {Mixed} s The value being converted
7022      * @return {String} The comparison value
7023      */
7024     asUCString : function(s) {
7025         return String(s).toUpperCase();
7026     },
7027     
7028     /**
7029      * Date sorting
7030      * @param {Mixed} s The value being converted
7031      * @return {Number} The comparison value
7032      */
7033     asDate : function(s) {
7034         if(!s){
7035             return 0;
7036         }
7037         if(s instanceof Date){
7038             return s.getTime();
7039         }
7040         return Date.parse(String(s));
7041     },
7042     
7043     /**
7044      * Float sorting
7045      * @param {Mixed} s The value being converted
7046      * @return {Float} The comparison value
7047      */
7048     asFloat : function(s) {
7049         var val = parseFloat(String(s).replace(/,/g, ""));
7050         if(isNaN(val)) val = 0;
7051         return val;
7052     },
7053     
7054     /**
7055      * Integer sorting
7056      * @param {Mixed} s The value being converted
7057      * @return {Number} The comparison value
7058      */
7059     asInt : function(s) {
7060         var val = parseInt(String(s).replace(/,/g, ""));
7061         if(isNaN(val)) val = 0;
7062         return val;
7063     }
7064 };/*
7065  * Based on:
7066  * Ext JS Library 1.1.1
7067  * Copyright(c) 2006-2007, Ext JS, LLC.
7068  *
7069  * Originally Released Under LGPL - original licence link has changed is not relivant.
7070  *
7071  * Fork - LGPL
7072  * <script type="text/javascript">
7073  */
7074
7075 /**
7076 * @class Roo.data.Record
7077  * Instances of this class encapsulate both record <em>definition</em> information, and record
7078  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7079  * to access Records cached in an {@link Roo.data.Store} object.<br>
7080  * <p>
7081  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7082  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7083  * objects.<br>
7084  * <p>
7085  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7086  * @constructor
7087  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7088  * {@link #create}. The parameters are the same.
7089  * @param {Array} data An associative Array of data values keyed by the field name.
7090  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7091  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7092  * not specified an integer id is generated.
7093  */
7094 Roo.data.Record = function(data, id){
7095     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7096     this.data = data;
7097 };
7098
7099 /**
7100  * Generate a constructor for a specific record layout.
7101  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7102  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7103  * Each field definition object may contain the following properties: <ul>
7104  * <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,
7105  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7106  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7107  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7108  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7109  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7110  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7111  * this may be omitted.</p></li>
7112  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7113  * <ul><li>auto (Default, implies no conversion)</li>
7114  * <li>string</li>
7115  * <li>int</li>
7116  * <li>float</li>
7117  * <li>boolean</li>
7118  * <li>date</li></ul></p></li>
7119  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7120  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7121  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7122  * by the Reader into an object that will be stored in the Record. It is passed the
7123  * following parameters:<ul>
7124  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7125  * </ul></p></li>
7126  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7127  * </ul>
7128  * <br>usage:<br><pre><code>
7129 var TopicRecord = Roo.data.Record.create(
7130     {name: 'title', mapping: 'topic_title'},
7131     {name: 'author', mapping: 'username'},
7132     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7133     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7134     {name: 'lastPoster', mapping: 'user2'},
7135     {name: 'excerpt', mapping: 'post_text'}
7136 );
7137
7138 var myNewRecord = new TopicRecord({
7139     title: 'Do my job please',
7140     author: 'noobie',
7141     totalPosts: 1,
7142     lastPost: new Date(),
7143     lastPoster: 'Animal',
7144     excerpt: 'No way dude!'
7145 });
7146 myStore.add(myNewRecord);
7147 </code></pre>
7148  * @method create
7149  * @static
7150  */
7151 Roo.data.Record.create = function(o){
7152     var f = function(){
7153         f.superclass.constructor.apply(this, arguments);
7154     };
7155     Roo.extend(f, Roo.data.Record);
7156     var p = f.prototype;
7157     p.fields = new Roo.util.MixedCollection(false, function(field){
7158         return field.name;
7159     });
7160     for(var i = 0, len = o.length; i < len; i++){
7161         p.fields.add(new Roo.data.Field(o[i]));
7162     }
7163     f.getField = function(name){
7164         return p.fields.get(name);  
7165     };
7166     return f;
7167 };
7168
7169 Roo.data.Record.AUTO_ID = 1000;
7170 Roo.data.Record.EDIT = 'edit';
7171 Roo.data.Record.REJECT = 'reject';
7172 Roo.data.Record.COMMIT = 'commit';
7173
7174 Roo.data.Record.prototype = {
7175     /**
7176      * Readonly flag - true if this record has been modified.
7177      * @type Boolean
7178      */
7179     dirty : false,
7180     editing : false,
7181     error: null,
7182     modified: null,
7183
7184     // private
7185     join : function(store){
7186         this.store = store;
7187     },
7188
7189     /**
7190      * Set the named field to the specified value.
7191      * @param {String} name The name of the field to set.
7192      * @param {Object} value The value to set the field to.
7193      */
7194     set : function(name, value){
7195         if(this.data[name] == value){
7196             return;
7197         }
7198         this.dirty = true;
7199         if(!this.modified){
7200             this.modified = {};
7201         }
7202         if(typeof this.modified[name] == 'undefined'){
7203             this.modified[name] = this.data[name];
7204         }
7205         this.data[name] = value;
7206         if(!this.editing && this.store){
7207             this.store.afterEdit(this);
7208         }       
7209     },
7210
7211     /**
7212      * Get the value of the named field.
7213      * @param {String} name The name of the field to get the value of.
7214      * @return {Object} The value of the field.
7215      */
7216     get : function(name){
7217         return this.data[name]; 
7218     },
7219
7220     // private
7221     beginEdit : function(){
7222         this.editing = true;
7223         this.modified = {}; 
7224     },
7225
7226     // private
7227     cancelEdit : function(){
7228         this.editing = false;
7229         delete this.modified;
7230     },
7231
7232     // private
7233     endEdit : function(){
7234         this.editing = false;
7235         if(this.dirty && this.store){
7236             this.store.afterEdit(this);
7237         }
7238     },
7239
7240     /**
7241      * Usually called by the {@link Roo.data.Store} which owns the Record.
7242      * Rejects all changes made to the Record since either creation, or the last commit operation.
7243      * Modified fields are reverted to their original values.
7244      * <p>
7245      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7246      * of reject operations.
7247      */
7248     reject : function(){
7249         var m = this.modified;
7250         for(var n in m){
7251             if(typeof m[n] != "function"){
7252                 this.data[n] = m[n];
7253             }
7254         }
7255         this.dirty = false;
7256         delete this.modified;
7257         this.editing = false;
7258         if(this.store){
7259             this.store.afterReject(this);
7260         }
7261     },
7262
7263     /**
7264      * Usually called by the {@link Roo.data.Store} which owns the Record.
7265      * Commits all changes made to the Record since either creation, or the last commit operation.
7266      * <p>
7267      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7268      * of commit operations.
7269      */
7270     commit : function(){
7271         this.dirty = false;
7272         delete this.modified;
7273         this.editing = false;
7274         if(this.store){
7275             this.store.afterCommit(this);
7276         }
7277     },
7278
7279     // private
7280     hasError : function(){
7281         return this.error != null;
7282     },
7283
7284     // private
7285     clearError : function(){
7286         this.error = null;
7287     },
7288
7289     /**
7290      * Creates a copy of this record.
7291      * @param {String} id (optional) A new record id if you don't want to use this record's id
7292      * @return {Record}
7293      */
7294     copy : function(newId) {
7295         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7296     }
7297 };/*
7298  * Based on:
7299  * Ext JS Library 1.1.1
7300  * Copyright(c) 2006-2007, Ext JS, LLC.
7301  *
7302  * Originally Released Under LGPL - original licence link has changed is not relivant.
7303  *
7304  * Fork - LGPL
7305  * <script type="text/javascript">
7306  */
7307
7308
7309
7310 /**
7311  * @class Roo.data.Store
7312  * @extends Roo.util.Observable
7313  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7314  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7315  * <p>
7316  * 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
7317  * has no knowledge of the format of the data returned by the Proxy.<br>
7318  * <p>
7319  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7320  * instances from the data object. These records are cached and made available through accessor functions.
7321  * @constructor
7322  * Creates a new Store.
7323  * @param {Object} config A config object containing the objects needed for the Store to access data,
7324  * and read the data into Records.
7325  */
7326 Roo.data.Store = function(config){
7327     this.data = new Roo.util.MixedCollection(false);
7328     this.data.getKey = function(o){
7329         return o.id;
7330     };
7331     this.baseParams = {};
7332     // private
7333     this.paramNames = {
7334         "start" : "start",
7335         "limit" : "limit",
7336         "sort" : "sort",
7337         "dir" : "dir",
7338         "multisort" : "_multisort"
7339     };
7340
7341     if(config && config.data){
7342         this.inlineData = config.data;
7343         delete config.data;
7344     }
7345
7346     Roo.apply(this, config);
7347     
7348     if(this.reader){ // reader passed
7349         this.reader = Roo.factory(this.reader, Roo.data);
7350         this.reader.xmodule = this.xmodule || false;
7351         if(!this.recordType){
7352             this.recordType = this.reader.recordType;
7353         }
7354         if(this.reader.onMetaChange){
7355             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7356         }
7357     }
7358
7359     if(this.recordType){
7360         this.fields = this.recordType.prototype.fields;
7361     }
7362     this.modified = [];
7363
7364     this.addEvents({
7365         /**
7366          * @event datachanged
7367          * Fires when the data cache has changed, and a widget which is using this Store
7368          * as a Record cache should refresh its view.
7369          * @param {Store} this
7370          */
7371         datachanged : true,
7372         /**
7373          * @event metachange
7374          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7375          * @param {Store} this
7376          * @param {Object} meta The JSON metadata
7377          */
7378         metachange : true,
7379         /**
7380          * @event add
7381          * Fires when Records have been added to the Store
7382          * @param {Store} this
7383          * @param {Roo.data.Record[]} records The array of Records added
7384          * @param {Number} index The index at which the record(s) were added
7385          */
7386         add : true,
7387         /**
7388          * @event remove
7389          * Fires when a Record has been removed from the Store
7390          * @param {Store} this
7391          * @param {Roo.data.Record} record The Record that was removed
7392          * @param {Number} index The index at which the record was removed
7393          */
7394         remove : true,
7395         /**
7396          * @event update
7397          * Fires when a Record has been updated
7398          * @param {Store} this
7399          * @param {Roo.data.Record} record The Record that was updated
7400          * @param {String} operation The update operation being performed.  Value may be one of:
7401          * <pre><code>
7402  Roo.data.Record.EDIT
7403  Roo.data.Record.REJECT
7404  Roo.data.Record.COMMIT
7405          * </code></pre>
7406          */
7407         update : true,
7408         /**
7409          * @event clear
7410          * Fires when the data cache has been cleared.
7411          * @param {Store} this
7412          */
7413         clear : true,
7414         /**
7415          * @event beforeload
7416          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7417          * the load action will be canceled.
7418          * @param {Store} this
7419          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7420          */
7421         beforeload : true,
7422         /**
7423          * @event beforeloadadd
7424          * Fires after a new set of Records has been loaded.
7425          * @param {Store} this
7426          * @param {Roo.data.Record[]} records The Records that were loaded
7427          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7428          */
7429         beforeloadadd : true,
7430         /**
7431          * @event load
7432          * Fires after a new set of Records has been loaded, before they are added to the store.
7433          * @param {Store} this
7434          * @param {Roo.data.Record[]} records The Records that were loaded
7435          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7436          * @params {Object} return from reader
7437          */
7438         load : true,
7439         /**
7440          * @event loadexception
7441          * Fires if an exception occurs in the Proxy during loading.
7442          * Called with the signature of the Proxy's "loadexception" event.
7443          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7444          * 
7445          * @param {Proxy} 
7446          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7447          * @param {Object} load options 
7448          * @param {Object} jsonData from your request (normally this contains the Exception)
7449          */
7450         loadexception : true
7451     });
7452     
7453     if(this.proxy){
7454         this.proxy = Roo.factory(this.proxy, Roo.data);
7455         this.proxy.xmodule = this.xmodule || false;
7456         this.relayEvents(this.proxy,  ["loadexception"]);
7457     }
7458     this.sortToggle = {};
7459     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7460
7461     Roo.data.Store.superclass.constructor.call(this);
7462
7463     if(this.inlineData){
7464         this.loadData(this.inlineData);
7465         delete this.inlineData;
7466     }
7467 };
7468
7469 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7470      /**
7471     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7472     * without a remote query - used by combo/forms at present.
7473     */
7474     
7475     /**
7476     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7477     */
7478     /**
7479     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7480     */
7481     /**
7482     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7483     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7484     */
7485     /**
7486     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7487     * on any HTTP request
7488     */
7489     /**
7490     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7491     */
7492     /**
7493     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7494     */
7495     multiSort: false,
7496     /**
7497     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7498     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7499     */
7500     remoteSort : false,
7501
7502     /**
7503     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7504      * loaded or when a record is removed. (defaults to false).
7505     */
7506     pruneModifiedRecords : false,
7507
7508     // private
7509     lastOptions : null,
7510
7511     /**
7512      * Add Records to the Store and fires the add event.
7513      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7514      */
7515     add : function(records){
7516         records = [].concat(records);
7517         for(var i = 0, len = records.length; i < len; i++){
7518             records[i].join(this);
7519         }
7520         var index = this.data.length;
7521         this.data.addAll(records);
7522         this.fireEvent("add", this, records, index);
7523     },
7524
7525     /**
7526      * Remove a Record from the Store and fires the remove event.
7527      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7528      */
7529     remove : function(record){
7530         var index = this.data.indexOf(record);
7531         this.data.removeAt(index);
7532         if(this.pruneModifiedRecords){
7533             this.modified.remove(record);
7534         }
7535         this.fireEvent("remove", this, record, index);
7536     },
7537
7538     /**
7539      * Remove all Records from the Store and fires the clear event.
7540      */
7541     removeAll : function(){
7542         this.data.clear();
7543         if(this.pruneModifiedRecords){
7544             this.modified = [];
7545         }
7546         this.fireEvent("clear", this);
7547     },
7548
7549     /**
7550      * Inserts Records to the Store at the given index and fires the add event.
7551      * @param {Number} index The start index at which to insert the passed Records.
7552      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7553      */
7554     insert : function(index, records){
7555         records = [].concat(records);
7556         for(var i = 0, len = records.length; i < len; i++){
7557             this.data.insert(index, records[i]);
7558             records[i].join(this);
7559         }
7560         this.fireEvent("add", this, records, index);
7561     },
7562
7563     /**
7564      * Get the index within the cache of the passed Record.
7565      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7566      * @return {Number} The index of the passed Record. Returns -1 if not found.
7567      */
7568     indexOf : function(record){
7569         return this.data.indexOf(record);
7570     },
7571
7572     /**
7573      * Get the index within the cache of the Record with the passed id.
7574      * @param {String} id The id of the Record to find.
7575      * @return {Number} The index of the Record. Returns -1 if not found.
7576      */
7577     indexOfId : function(id){
7578         return this.data.indexOfKey(id);
7579     },
7580
7581     /**
7582      * Get the Record with the specified id.
7583      * @param {String} id The id of the Record to find.
7584      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7585      */
7586     getById : function(id){
7587         return this.data.key(id);
7588     },
7589
7590     /**
7591      * Get the Record at the specified index.
7592      * @param {Number} index The index of the Record to find.
7593      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7594      */
7595     getAt : function(index){
7596         return this.data.itemAt(index);
7597     },
7598
7599     /**
7600      * Returns a range of Records between specified indices.
7601      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7602      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7603      * @return {Roo.data.Record[]} An array of Records
7604      */
7605     getRange : function(start, end){
7606         return this.data.getRange(start, end);
7607     },
7608
7609     // private
7610     storeOptions : function(o){
7611         o = Roo.apply({}, o);
7612         delete o.callback;
7613         delete o.scope;
7614         this.lastOptions = o;
7615     },
7616
7617     /**
7618      * Loads the Record cache from the configured Proxy using the configured Reader.
7619      * <p>
7620      * If using remote paging, then the first load call must specify the <em>start</em>
7621      * and <em>limit</em> properties in the options.params property to establish the initial
7622      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7623      * <p>
7624      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7625      * and this call will return before the new data has been loaded. Perform any post-processing
7626      * in a callback function, or in a "load" event handler.</strong>
7627      * <p>
7628      * @param {Object} options An object containing properties which control loading options:<ul>
7629      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7630      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7631      * passed the following arguments:<ul>
7632      * <li>r : Roo.data.Record[]</li>
7633      * <li>options: Options object from the load call</li>
7634      * <li>success: Boolean success indicator</li></ul></li>
7635      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7636      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7637      * </ul>
7638      */
7639     load : function(options){
7640         options = options || {};
7641         if(this.fireEvent("beforeload", this, options) !== false){
7642             this.storeOptions(options);
7643             var p = Roo.apply(options.params || {}, this.baseParams);
7644             // if meta was not loaded from remote source.. try requesting it.
7645             if (!this.reader.metaFromRemote) {
7646                 p._requestMeta = 1;
7647             }
7648             if(this.sortInfo && this.remoteSort){
7649                 var pn = this.paramNames;
7650                 p[pn["sort"]] = this.sortInfo.field;
7651                 p[pn["dir"]] = this.sortInfo.direction;
7652             }
7653             if (this.multiSort) {
7654                 var pn = this.paramNames;
7655                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7656             }
7657             
7658             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7659         }
7660     },
7661
7662     /**
7663      * Reloads the Record cache from the configured Proxy using the configured Reader and
7664      * the options from the last load operation performed.
7665      * @param {Object} options (optional) An object containing properties which may override the options
7666      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7667      * the most recently used options are reused).
7668      */
7669     reload : function(options){
7670         this.load(Roo.applyIf(options||{}, this.lastOptions));
7671     },
7672
7673     // private
7674     // Called as a callback by the Reader during a load operation.
7675     loadRecords : function(o, options, success){
7676         if(!o || success === false){
7677             if(success !== false){
7678                 this.fireEvent("load", this, [], options, o);
7679             }
7680             if(options.callback){
7681                 options.callback.call(options.scope || this, [], options, false);
7682             }
7683             return;
7684         }
7685         // if data returned failure - throw an exception.
7686         if (o.success === false) {
7687             // show a message if no listener is registered.
7688             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7689                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7690             }
7691             // loadmask wil be hooked into this..
7692             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7693             return;
7694         }
7695         var r = o.records, t = o.totalRecords || r.length;
7696         
7697         this.fireEvent("beforeloadadd", this, r, options, o);
7698         
7699         if(!options || options.add !== true){
7700             if(this.pruneModifiedRecords){
7701                 this.modified = [];
7702             }
7703             for(var i = 0, len = r.length; i < len; i++){
7704                 r[i].join(this);
7705             }
7706             if(this.snapshot){
7707                 this.data = this.snapshot;
7708                 delete this.snapshot;
7709             }
7710             this.data.clear();
7711             this.data.addAll(r);
7712             this.totalLength = t;
7713             this.applySort();
7714             this.fireEvent("datachanged", this);
7715         }else{
7716             this.totalLength = Math.max(t, this.data.length+r.length);
7717             this.add(r);
7718         }
7719         this.fireEvent("load", this, r, options, o);
7720         if(options.callback){
7721             options.callback.call(options.scope || this, r, options, true);
7722         }
7723     },
7724
7725
7726     /**
7727      * Loads data from a passed data block. A Reader which understands the format of the data
7728      * must have been configured in the constructor.
7729      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7730      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7731      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7732      */
7733     loadData : function(o, append){
7734         var r = this.reader.readRecords(o);
7735         this.loadRecords(r, {add: append}, true);
7736     },
7737
7738     /**
7739      * Gets the number of cached records.
7740      * <p>
7741      * <em>If using paging, this may not be the total size of the dataset. If the data object
7742      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7743      * the data set size</em>
7744      */
7745     getCount : function(){
7746         return this.data.length || 0;
7747     },
7748
7749     /**
7750      * Gets the total number of records in the dataset as returned by the server.
7751      * <p>
7752      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7753      * the dataset size</em>
7754      */
7755     getTotalCount : function(){
7756         return this.totalLength || 0;
7757     },
7758
7759     /**
7760      * Returns the sort state of the Store as an object with two properties:
7761      * <pre><code>
7762  field {String} The name of the field by which the Records are sorted
7763  direction {String} The sort order, "ASC" or "DESC"
7764      * </code></pre>
7765      */
7766     getSortState : function(){
7767         return this.sortInfo;
7768     },
7769
7770     // private
7771     applySort : function(){
7772         if(this.sortInfo && !this.remoteSort){
7773             var s = this.sortInfo, f = s.field;
7774             var st = this.fields.get(f).sortType;
7775             var fn = function(r1, r2){
7776                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7777                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7778             };
7779             this.data.sort(s.direction, fn);
7780             if(this.snapshot && this.snapshot != this.data){
7781                 this.snapshot.sort(s.direction, fn);
7782             }
7783         }
7784     },
7785
7786     /**
7787      * Sets the default sort column and order to be used by the next load operation.
7788      * @param {String} fieldName The name of the field to sort by.
7789      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7790      */
7791     setDefaultSort : function(field, dir){
7792         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7793     },
7794
7795     /**
7796      * Sort the Records.
7797      * If remote sorting is used, the sort is performed on the server, and the cache is
7798      * reloaded. If local sorting is used, the cache is sorted internally.
7799      * @param {String} fieldName The name of the field to sort by.
7800      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7801      */
7802     sort : function(fieldName, dir){
7803         var f = this.fields.get(fieldName);
7804         if(!dir){
7805             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7806             
7807             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7808                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7809             }else{
7810                 dir = f.sortDir;
7811             }
7812         }
7813         this.sortToggle[f.name] = dir;
7814         this.sortInfo = {field: f.name, direction: dir};
7815         if(!this.remoteSort){
7816             this.applySort();
7817             this.fireEvent("datachanged", this);
7818         }else{
7819             this.load(this.lastOptions);
7820         }
7821     },
7822
7823     /**
7824      * Calls the specified function for each of the Records in the cache.
7825      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7826      * Returning <em>false</em> aborts and exits the iteration.
7827      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7828      */
7829     each : function(fn, scope){
7830         this.data.each(fn, scope);
7831     },
7832
7833     /**
7834      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7835      * (e.g., during paging).
7836      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7837      */
7838     getModifiedRecords : function(){
7839         return this.modified;
7840     },
7841
7842     // private
7843     createFilterFn : function(property, value, anyMatch){
7844         if(!value.exec){ // not a regex
7845             value = String(value);
7846             if(value.length == 0){
7847                 return false;
7848             }
7849             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7850         }
7851         return function(r){
7852             return value.test(r.data[property]);
7853         };
7854     },
7855
7856     /**
7857      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7858      * @param {String} property A field on your records
7859      * @param {Number} start The record index to start at (defaults to 0)
7860      * @param {Number} end The last record index to include (defaults to length - 1)
7861      * @return {Number} The sum
7862      */
7863     sum : function(property, start, end){
7864         var rs = this.data.items, v = 0;
7865         start = start || 0;
7866         end = (end || end === 0) ? end : rs.length-1;
7867
7868         for(var i = start; i <= end; i++){
7869             v += (rs[i].data[property] || 0);
7870         }
7871         return v;
7872     },
7873
7874     /**
7875      * Filter the records by a specified property.
7876      * @param {String} field A field on your records
7877      * @param {String/RegExp} value Either a string that the field
7878      * should start with or a RegExp to test against the field
7879      * @param {Boolean} anyMatch True to match any part not just the beginning
7880      */
7881     filter : function(property, value, anyMatch){
7882         var fn = this.createFilterFn(property, value, anyMatch);
7883         return fn ? this.filterBy(fn) : this.clearFilter();
7884     },
7885
7886     /**
7887      * Filter by a function. The specified function will be called with each
7888      * record in this data source. If the function returns true the record is included,
7889      * otherwise it is filtered.
7890      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7891      * @param {Object} scope (optional) The scope of the function (defaults to this)
7892      */
7893     filterBy : function(fn, scope){
7894         this.snapshot = this.snapshot || this.data;
7895         this.data = this.queryBy(fn, scope||this);
7896         this.fireEvent("datachanged", this);
7897     },
7898
7899     /**
7900      * Query the records by a specified property.
7901      * @param {String} field A field on your records
7902      * @param {String/RegExp} value Either a string that the field
7903      * should start with or a RegExp to test against the field
7904      * @param {Boolean} anyMatch True to match any part not just the beginning
7905      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7906      */
7907     query : function(property, value, anyMatch){
7908         var fn = this.createFilterFn(property, value, anyMatch);
7909         return fn ? this.queryBy(fn) : this.data.clone();
7910     },
7911
7912     /**
7913      * Query by a function. The specified function will be called with each
7914      * record in this data source. If the function returns true the record is included
7915      * in the results.
7916      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7917      * @param {Object} scope (optional) The scope of the function (defaults to this)
7918       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7919      **/
7920     queryBy : function(fn, scope){
7921         var data = this.snapshot || this.data;
7922         return data.filterBy(fn, scope||this);
7923     },
7924
7925     /**
7926      * Collects unique values for a particular dataIndex from this store.
7927      * @param {String} dataIndex The property to collect
7928      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7929      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7930      * @return {Array} An array of the unique values
7931      **/
7932     collect : function(dataIndex, allowNull, bypassFilter){
7933         var d = (bypassFilter === true && this.snapshot) ?
7934                 this.snapshot.items : this.data.items;
7935         var v, sv, r = [], l = {};
7936         for(var i = 0, len = d.length; i < len; i++){
7937             v = d[i].data[dataIndex];
7938             sv = String(v);
7939             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7940                 l[sv] = true;
7941                 r[r.length] = v;
7942             }
7943         }
7944         return r;
7945     },
7946
7947     /**
7948      * Revert to a view of the Record cache with no filtering applied.
7949      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7950      */
7951     clearFilter : function(suppressEvent){
7952         if(this.snapshot && this.snapshot != this.data){
7953             this.data = this.snapshot;
7954             delete this.snapshot;
7955             if(suppressEvent !== true){
7956                 this.fireEvent("datachanged", this);
7957             }
7958         }
7959     },
7960
7961     // private
7962     afterEdit : function(record){
7963         if(this.modified.indexOf(record) == -1){
7964             this.modified.push(record);
7965         }
7966         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7967     },
7968     
7969     // private
7970     afterReject : function(record){
7971         this.modified.remove(record);
7972         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7973     },
7974
7975     // private
7976     afterCommit : function(record){
7977         this.modified.remove(record);
7978         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7979     },
7980
7981     /**
7982      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7983      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7984      */
7985     commitChanges : function(){
7986         var m = this.modified.slice(0);
7987         this.modified = [];
7988         for(var i = 0, len = m.length; i < len; i++){
7989             m[i].commit();
7990         }
7991     },
7992
7993     /**
7994      * Cancel outstanding changes on all changed records.
7995      */
7996     rejectChanges : function(){
7997         var m = this.modified.slice(0);
7998         this.modified = [];
7999         for(var i = 0, len = m.length; i < len; i++){
8000             m[i].reject();
8001         }
8002     },
8003
8004     onMetaChange : function(meta, rtype, o){
8005         this.recordType = rtype;
8006         this.fields = rtype.prototype.fields;
8007         delete this.snapshot;
8008         this.sortInfo = meta.sortInfo || this.sortInfo;
8009         this.modified = [];
8010         this.fireEvent('metachange', this, this.reader.meta);
8011     },
8012     
8013     moveIndex : function(data, type)
8014     {
8015         var index = this.indexOf(data);
8016         
8017         var newIndex = index + type;
8018         
8019         this.remove(data);
8020         
8021         this.insert(newIndex, data);
8022         
8023     }
8024 });/*
8025  * Based on:
8026  * Ext JS Library 1.1.1
8027  * Copyright(c) 2006-2007, Ext JS, LLC.
8028  *
8029  * Originally Released Under LGPL - original licence link has changed is not relivant.
8030  *
8031  * Fork - LGPL
8032  * <script type="text/javascript">
8033  */
8034
8035 /**
8036  * @class Roo.data.SimpleStore
8037  * @extends Roo.data.Store
8038  * Small helper class to make creating Stores from Array data easier.
8039  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8040  * @cfg {Array} fields An array of field definition objects, or field name strings.
8041  * @cfg {Array} data The multi-dimensional array of data
8042  * @constructor
8043  * @param {Object} config
8044  */
8045 Roo.data.SimpleStore = function(config){
8046     Roo.data.SimpleStore.superclass.constructor.call(this, {
8047         isLocal : true,
8048         reader: new Roo.data.ArrayReader({
8049                 id: config.id
8050             },
8051             Roo.data.Record.create(config.fields)
8052         ),
8053         proxy : new Roo.data.MemoryProxy(config.data)
8054     });
8055     this.load();
8056 };
8057 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8058  * Based on:
8059  * Ext JS Library 1.1.1
8060  * Copyright(c) 2006-2007, Ext JS, LLC.
8061  *
8062  * Originally Released Under LGPL - original licence link has changed is not relivant.
8063  *
8064  * Fork - LGPL
8065  * <script type="text/javascript">
8066  */
8067
8068 /**
8069 /**
8070  * @extends Roo.data.Store
8071  * @class Roo.data.JsonStore
8072  * Small helper class to make creating Stores for JSON data easier. <br/>
8073 <pre><code>
8074 var store = new Roo.data.JsonStore({
8075     url: 'get-images.php',
8076     root: 'images',
8077     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8078 });
8079 </code></pre>
8080  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8081  * JsonReader and HttpProxy (unless inline data is provided).</b>
8082  * @cfg {Array} fields An array of field definition objects, or field name strings.
8083  * @constructor
8084  * @param {Object} config
8085  */
8086 Roo.data.JsonStore = function(c){
8087     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8088         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8089         reader: new Roo.data.JsonReader(c, c.fields)
8090     }));
8091 };
8092 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8093  * Based on:
8094  * Ext JS Library 1.1.1
8095  * Copyright(c) 2006-2007, Ext JS, LLC.
8096  *
8097  * Originally Released Under LGPL - original licence link has changed is not relivant.
8098  *
8099  * Fork - LGPL
8100  * <script type="text/javascript">
8101  */
8102
8103  
8104 Roo.data.Field = function(config){
8105     if(typeof config == "string"){
8106         config = {name: config};
8107     }
8108     Roo.apply(this, config);
8109     
8110     if(!this.type){
8111         this.type = "auto";
8112     }
8113     
8114     var st = Roo.data.SortTypes;
8115     // named sortTypes are supported, here we look them up
8116     if(typeof this.sortType == "string"){
8117         this.sortType = st[this.sortType];
8118     }
8119     
8120     // set default sortType for strings and dates
8121     if(!this.sortType){
8122         switch(this.type){
8123             case "string":
8124                 this.sortType = st.asUCString;
8125                 break;
8126             case "date":
8127                 this.sortType = st.asDate;
8128                 break;
8129             default:
8130                 this.sortType = st.none;
8131         }
8132     }
8133
8134     // define once
8135     var stripRe = /[\$,%]/g;
8136
8137     // prebuilt conversion function for this field, instead of
8138     // switching every time we're reading a value
8139     if(!this.convert){
8140         var cv, dateFormat = this.dateFormat;
8141         switch(this.type){
8142             case "":
8143             case "auto":
8144             case undefined:
8145                 cv = function(v){ return v; };
8146                 break;
8147             case "string":
8148                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8149                 break;
8150             case "int":
8151                 cv = function(v){
8152                     return v !== undefined && v !== null && v !== '' ?
8153                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8154                     };
8155                 break;
8156             case "float":
8157                 cv = function(v){
8158                     return v !== undefined && v !== null && v !== '' ?
8159                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8160                     };
8161                 break;
8162             case "bool":
8163             case "boolean":
8164                 cv = function(v){ return v === true || v === "true" || v == 1; };
8165                 break;
8166             case "date":
8167                 cv = function(v){
8168                     if(!v){
8169                         return '';
8170                     }
8171                     if(v instanceof Date){
8172                         return v;
8173                     }
8174                     if(dateFormat){
8175                         if(dateFormat == "timestamp"){
8176                             return new Date(v*1000);
8177                         }
8178                         return Date.parseDate(v, dateFormat);
8179                     }
8180                     var parsed = Date.parse(v);
8181                     return parsed ? new Date(parsed) : null;
8182                 };
8183              break;
8184             
8185         }
8186         this.convert = cv;
8187     }
8188 };
8189
8190 Roo.data.Field.prototype = {
8191     dateFormat: null,
8192     defaultValue: "",
8193     mapping: null,
8194     sortType : null,
8195     sortDir : "ASC"
8196 };/*
8197  * Based on:
8198  * Ext JS Library 1.1.1
8199  * Copyright(c) 2006-2007, Ext JS, LLC.
8200  *
8201  * Originally Released Under LGPL - original licence link has changed is not relivant.
8202  *
8203  * Fork - LGPL
8204  * <script type="text/javascript">
8205  */
8206  
8207 // Base class for reading structured data from a data source.  This class is intended to be
8208 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8209
8210 /**
8211  * @class Roo.data.DataReader
8212  * Base class for reading structured data from a data source.  This class is intended to be
8213  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8214  */
8215
8216 Roo.data.DataReader = function(meta, recordType){
8217     
8218     this.meta = meta;
8219     
8220     this.recordType = recordType instanceof Array ? 
8221         Roo.data.Record.create(recordType) : recordType;
8222 };
8223
8224 Roo.data.DataReader.prototype = {
8225      /**
8226      * Create an empty record
8227      * @param {Object} data (optional) - overlay some values
8228      * @return {Roo.data.Record} record created.
8229      */
8230     newRow :  function(d) {
8231         var da =  {};
8232         this.recordType.prototype.fields.each(function(c) {
8233             switch( c.type) {
8234                 case 'int' : da[c.name] = 0; break;
8235                 case 'date' : da[c.name] = new Date(); break;
8236                 case 'float' : da[c.name] = 0.0; break;
8237                 case 'boolean' : da[c.name] = false; break;
8238                 default : da[c.name] = ""; break;
8239             }
8240             
8241         });
8242         return new this.recordType(Roo.apply(da, d));
8243     }
8244     
8245 };/*
8246  * Based on:
8247  * Ext JS Library 1.1.1
8248  * Copyright(c) 2006-2007, Ext JS, LLC.
8249  *
8250  * Originally Released Under LGPL - original licence link has changed is not relivant.
8251  *
8252  * Fork - LGPL
8253  * <script type="text/javascript">
8254  */
8255
8256 /**
8257  * @class Roo.data.DataProxy
8258  * @extends Roo.data.Observable
8259  * This class is an abstract base class for implementations which provide retrieval of
8260  * unformatted data objects.<br>
8261  * <p>
8262  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8263  * (of the appropriate type which knows how to parse the data object) to provide a block of
8264  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8265  * <p>
8266  * Custom implementations must implement the load method as described in
8267  * {@link Roo.data.HttpProxy#load}.
8268  */
8269 Roo.data.DataProxy = function(){
8270     this.addEvents({
8271         /**
8272          * @event beforeload
8273          * Fires before a network request is made to retrieve a data object.
8274          * @param {Object} This DataProxy object.
8275          * @param {Object} params The params parameter to the load function.
8276          */
8277         beforeload : true,
8278         /**
8279          * @event load
8280          * Fires before the load method's callback is called.
8281          * @param {Object} This DataProxy object.
8282          * @param {Object} o The data object.
8283          * @param {Object} arg The callback argument object passed to the load function.
8284          */
8285         load : true,
8286         /**
8287          * @event loadexception
8288          * Fires if an Exception occurs during data retrieval.
8289          * @param {Object} This DataProxy object.
8290          * @param {Object} o The data object.
8291          * @param {Object} arg The callback argument object passed to the load function.
8292          * @param {Object} e The Exception.
8293          */
8294         loadexception : true
8295     });
8296     Roo.data.DataProxy.superclass.constructor.call(this);
8297 };
8298
8299 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8300
8301     /**
8302      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8303      */
8304 /*
8305  * Based on:
8306  * Ext JS Library 1.1.1
8307  * Copyright(c) 2006-2007, Ext JS, LLC.
8308  *
8309  * Originally Released Under LGPL - original licence link has changed is not relivant.
8310  *
8311  * Fork - LGPL
8312  * <script type="text/javascript">
8313  */
8314 /**
8315  * @class Roo.data.MemoryProxy
8316  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8317  * to the Reader when its load method is called.
8318  * @constructor
8319  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8320  */
8321 Roo.data.MemoryProxy = function(data){
8322     if (data.data) {
8323         data = data.data;
8324     }
8325     Roo.data.MemoryProxy.superclass.constructor.call(this);
8326     this.data = data;
8327 };
8328
8329 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8330     /**
8331      * Load data from the requested source (in this case an in-memory
8332      * data object passed to the constructor), read the data object into
8333      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8334      * process that block using the passed callback.
8335      * @param {Object} params This parameter is not used by the MemoryProxy class.
8336      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8337      * object into a block of Roo.data.Records.
8338      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8339      * The function must be passed <ul>
8340      * <li>The Record block object</li>
8341      * <li>The "arg" argument from the load function</li>
8342      * <li>A boolean success indicator</li>
8343      * </ul>
8344      * @param {Object} scope The scope in which to call the callback
8345      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8346      */
8347     load : function(params, reader, callback, scope, arg){
8348         params = params || {};
8349         var result;
8350         try {
8351             result = reader.readRecords(this.data);
8352         }catch(e){
8353             this.fireEvent("loadexception", this, arg, null, e);
8354             callback.call(scope, null, arg, false);
8355             return;
8356         }
8357         callback.call(scope, result, arg, true);
8358     },
8359     
8360     // private
8361     update : function(params, records){
8362         
8363     }
8364 });/*
8365  * Based on:
8366  * Ext JS Library 1.1.1
8367  * Copyright(c) 2006-2007, Ext JS, LLC.
8368  *
8369  * Originally Released Under LGPL - original licence link has changed is not relivant.
8370  *
8371  * Fork - LGPL
8372  * <script type="text/javascript">
8373  */
8374 /**
8375  * @class Roo.data.HttpProxy
8376  * @extends Roo.data.DataProxy
8377  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8378  * configured to reference a certain URL.<br><br>
8379  * <p>
8380  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8381  * from which the running page was served.<br><br>
8382  * <p>
8383  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8384  * <p>
8385  * Be aware that to enable the browser to parse an XML document, the server must set
8386  * the Content-Type header in the HTTP response to "text/xml".
8387  * @constructor
8388  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8389  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8390  * will be used to make the request.
8391  */
8392 Roo.data.HttpProxy = function(conn){
8393     Roo.data.HttpProxy.superclass.constructor.call(this);
8394     // is conn a conn config or a real conn?
8395     this.conn = conn;
8396     this.useAjax = !conn || !conn.events;
8397   
8398 };
8399
8400 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8401     // thse are take from connection...
8402     
8403     /**
8404      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8405      */
8406     /**
8407      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8408      * extra parameters to each request made by this object. (defaults to undefined)
8409      */
8410     /**
8411      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8412      *  to each request made by this object. (defaults to undefined)
8413      */
8414     /**
8415      * @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)
8416      */
8417     /**
8418      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8419      */
8420      /**
8421      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8422      * @type Boolean
8423      */
8424   
8425
8426     /**
8427      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8428      * @type Boolean
8429      */
8430     /**
8431      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8432      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8433      * a finer-grained basis than the DataProxy events.
8434      */
8435     getConnection : function(){
8436         return this.useAjax ? Roo.Ajax : this.conn;
8437     },
8438
8439     /**
8440      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8441      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8442      * process that block using the passed callback.
8443      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8444      * for the request to the remote server.
8445      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8446      * object into a block of Roo.data.Records.
8447      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8448      * The function must be passed <ul>
8449      * <li>The Record block object</li>
8450      * <li>The "arg" argument from the load function</li>
8451      * <li>A boolean success indicator</li>
8452      * </ul>
8453      * @param {Object} scope The scope in which to call the callback
8454      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8455      */
8456     load : function(params, reader, callback, scope, arg){
8457         if(this.fireEvent("beforeload", this, params) !== false){
8458             var  o = {
8459                 params : params || {},
8460                 request: {
8461                     callback : callback,
8462                     scope : scope,
8463                     arg : arg
8464                 },
8465                 reader: reader,
8466                 callback : this.loadResponse,
8467                 scope: this
8468             };
8469             if(this.useAjax){
8470                 Roo.applyIf(o, this.conn);
8471                 if(this.activeRequest){
8472                     Roo.Ajax.abort(this.activeRequest);
8473                 }
8474                 this.activeRequest = Roo.Ajax.request(o);
8475             }else{
8476                 this.conn.request(o);
8477             }
8478         }else{
8479             callback.call(scope||this, null, arg, false);
8480         }
8481     },
8482
8483     // private
8484     loadResponse : function(o, success, response){
8485         delete this.activeRequest;
8486         if(!success){
8487             this.fireEvent("loadexception", this, o, response);
8488             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8489             return;
8490         }
8491         var result;
8492         try {
8493             result = o.reader.read(response);
8494         }catch(e){
8495             this.fireEvent("loadexception", this, o, response, e);
8496             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8497             return;
8498         }
8499         
8500         this.fireEvent("load", this, o, o.request.arg);
8501         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8502     },
8503
8504     // private
8505     update : function(dataSet){
8506
8507     },
8508
8509     // private
8510     updateResponse : function(dataSet){
8511
8512     }
8513 });/*
8514  * Based on:
8515  * Ext JS Library 1.1.1
8516  * Copyright(c) 2006-2007, Ext JS, LLC.
8517  *
8518  * Originally Released Under LGPL - original licence link has changed is not relivant.
8519  *
8520  * Fork - LGPL
8521  * <script type="text/javascript">
8522  */
8523
8524 /**
8525  * @class Roo.data.ScriptTagProxy
8526  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8527  * other than the originating domain of the running page.<br><br>
8528  * <p>
8529  * <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
8530  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8531  * <p>
8532  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8533  * source code that is used as the source inside a &lt;script> tag.<br><br>
8534  * <p>
8535  * In order for the browser to process the returned data, the server must wrap the data object
8536  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8537  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8538  * depending on whether the callback name was passed:
8539  * <p>
8540  * <pre><code>
8541 boolean scriptTag = false;
8542 String cb = request.getParameter("callback");
8543 if (cb != null) {
8544     scriptTag = true;
8545     response.setContentType("text/javascript");
8546 } else {
8547     response.setContentType("application/x-json");
8548 }
8549 Writer out = response.getWriter();
8550 if (scriptTag) {
8551     out.write(cb + "(");
8552 }
8553 out.print(dataBlock.toJsonString());
8554 if (scriptTag) {
8555     out.write(");");
8556 }
8557 </pre></code>
8558  *
8559  * @constructor
8560  * @param {Object} config A configuration object.
8561  */
8562 Roo.data.ScriptTagProxy = function(config){
8563     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8564     Roo.apply(this, config);
8565     this.head = document.getElementsByTagName("head")[0];
8566 };
8567
8568 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8569
8570 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8571     /**
8572      * @cfg {String} url The URL from which to request the data object.
8573      */
8574     /**
8575      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8576      */
8577     timeout : 30000,
8578     /**
8579      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8580      * the server the name of the callback function set up by the load call to process the returned data object.
8581      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8582      * javascript output which calls this named function passing the data object as its only parameter.
8583      */
8584     callbackParam : "callback",
8585     /**
8586      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8587      * name to the request.
8588      */
8589     nocache : true,
8590
8591     /**
8592      * Load data from the configured URL, read the data object into
8593      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8594      * process that block using the passed callback.
8595      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8596      * for the request to the remote server.
8597      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8598      * object into a block of Roo.data.Records.
8599      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8600      * The function must be passed <ul>
8601      * <li>The Record block object</li>
8602      * <li>The "arg" argument from the load function</li>
8603      * <li>A boolean success indicator</li>
8604      * </ul>
8605      * @param {Object} scope The scope in which to call the callback
8606      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8607      */
8608     load : function(params, reader, callback, scope, arg){
8609         if(this.fireEvent("beforeload", this, params) !== false){
8610
8611             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8612
8613             var url = this.url;
8614             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8615             if(this.nocache){
8616                 url += "&_dc=" + (new Date().getTime());
8617             }
8618             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8619             var trans = {
8620                 id : transId,
8621                 cb : "stcCallback"+transId,
8622                 scriptId : "stcScript"+transId,
8623                 params : params,
8624                 arg : arg,
8625                 url : url,
8626                 callback : callback,
8627                 scope : scope,
8628                 reader : reader
8629             };
8630             var conn = this;
8631
8632             window[trans.cb] = function(o){
8633                 conn.handleResponse(o, trans);
8634             };
8635
8636             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8637
8638             if(this.autoAbort !== false){
8639                 this.abort();
8640             }
8641
8642             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8643
8644             var script = document.createElement("script");
8645             script.setAttribute("src", url);
8646             script.setAttribute("type", "text/javascript");
8647             script.setAttribute("id", trans.scriptId);
8648             this.head.appendChild(script);
8649
8650             this.trans = trans;
8651         }else{
8652             callback.call(scope||this, null, arg, false);
8653         }
8654     },
8655
8656     // private
8657     isLoading : function(){
8658         return this.trans ? true : false;
8659     },
8660
8661     /**
8662      * Abort the current server request.
8663      */
8664     abort : function(){
8665         if(this.isLoading()){
8666             this.destroyTrans(this.trans);
8667         }
8668     },
8669
8670     // private
8671     destroyTrans : function(trans, isLoaded){
8672         this.head.removeChild(document.getElementById(trans.scriptId));
8673         clearTimeout(trans.timeoutId);
8674         if(isLoaded){
8675             window[trans.cb] = undefined;
8676             try{
8677                 delete window[trans.cb];
8678             }catch(e){}
8679         }else{
8680             // if hasn't been loaded, wait for load to remove it to prevent script error
8681             window[trans.cb] = function(){
8682                 window[trans.cb] = undefined;
8683                 try{
8684                     delete window[trans.cb];
8685                 }catch(e){}
8686             };
8687         }
8688     },
8689
8690     // private
8691     handleResponse : function(o, trans){
8692         this.trans = false;
8693         this.destroyTrans(trans, true);
8694         var result;
8695         try {
8696             result = trans.reader.readRecords(o);
8697         }catch(e){
8698             this.fireEvent("loadexception", this, o, trans.arg, e);
8699             trans.callback.call(trans.scope||window, null, trans.arg, false);
8700             return;
8701         }
8702         this.fireEvent("load", this, o, trans.arg);
8703         trans.callback.call(trans.scope||window, result, trans.arg, true);
8704     },
8705
8706     // private
8707     handleFailure : function(trans){
8708         this.trans = false;
8709         this.destroyTrans(trans, false);
8710         this.fireEvent("loadexception", this, null, trans.arg);
8711         trans.callback.call(trans.scope||window, null, trans.arg, false);
8712     }
8713 });/*
8714  * Based on:
8715  * Ext JS Library 1.1.1
8716  * Copyright(c) 2006-2007, Ext JS, LLC.
8717  *
8718  * Originally Released Under LGPL - original licence link has changed is not relivant.
8719  *
8720  * Fork - LGPL
8721  * <script type="text/javascript">
8722  */
8723
8724 /**
8725  * @class Roo.data.JsonReader
8726  * @extends Roo.data.DataReader
8727  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8728  * based on mappings in a provided Roo.data.Record constructor.
8729  * 
8730  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8731  * in the reply previously. 
8732  * 
8733  * <p>
8734  * Example code:
8735  * <pre><code>
8736 var RecordDef = Roo.data.Record.create([
8737     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8738     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8739 ]);
8740 var myReader = new Roo.data.JsonReader({
8741     totalProperty: "results",    // The property which contains the total dataset size (optional)
8742     root: "rows",                // The property which contains an Array of row objects
8743     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8744 }, RecordDef);
8745 </code></pre>
8746  * <p>
8747  * This would consume a JSON file like this:
8748  * <pre><code>
8749 { 'results': 2, 'rows': [
8750     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8751     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8752 }
8753 </code></pre>
8754  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8755  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8756  * paged from the remote server.
8757  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8758  * @cfg {String} root name of the property which contains the Array of row objects.
8759  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8760  * @constructor
8761  * Create a new JsonReader
8762  * @param {Object} meta Metadata configuration options
8763  * @param {Object} recordType Either an Array of field definition objects,
8764  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8765  */
8766 Roo.data.JsonReader = function(meta, recordType){
8767     
8768     meta = meta || {};
8769     // set some defaults:
8770     Roo.applyIf(meta, {
8771         totalProperty: 'total',
8772         successProperty : 'success',
8773         root : 'data',
8774         id : 'id'
8775     });
8776     
8777     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8778 };
8779 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8780     
8781     /**
8782      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8783      * Used by Store query builder to append _requestMeta to params.
8784      * 
8785      */
8786     metaFromRemote : false,
8787     /**
8788      * This method is only used by a DataProxy which has retrieved data from a remote server.
8789      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8790      * @return {Object} data A data block which is used by an Roo.data.Store object as
8791      * a cache of Roo.data.Records.
8792      */
8793     read : function(response){
8794         var json = response.responseText;
8795        
8796         var o = /* eval:var:o */ eval("("+json+")");
8797         if(!o) {
8798             throw {message: "JsonReader.read: Json object not found"};
8799         }
8800         
8801         if(o.metaData){
8802             
8803             delete this.ef;
8804             this.metaFromRemote = true;
8805             this.meta = o.metaData;
8806             this.recordType = Roo.data.Record.create(o.metaData.fields);
8807             this.onMetaChange(this.meta, this.recordType, o);
8808         }
8809         return this.readRecords(o);
8810     },
8811
8812     // private function a store will implement
8813     onMetaChange : function(meta, recordType, o){
8814
8815     },
8816
8817     /**
8818          * @ignore
8819          */
8820     simpleAccess: function(obj, subsc) {
8821         return obj[subsc];
8822     },
8823
8824         /**
8825          * @ignore
8826          */
8827     getJsonAccessor: function(){
8828         var re = /[\[\.]/;
8829         return function(expr) {
8830             try {
8831                 return(re.test(expr))
8832                     ? new Function("obj", "return obj." + expr)
8833                     : function(obj){
8834                         return obj[expr];
8835                     };
8836             } catch(e){}
8837             return Roo.emptyFn;
8838         };
8839     }(),
8840
8841     /**
8842      * Create a data block containing Roo.data.Records from an XML document.
8843      * @param {Object} o An object which contains an Array of row objects in the property specified
8844      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8845      * which contains the total size of the dataset.
8846      * @return {Object} data A data block which is used by an Roo.data.Store object as
8847      * a cache of Roo.data.Records.
8848      */
8849     readRecords : function(o){
8850         /**
8851          * After any data loads, the raw JSON data is available for further custom processing.
8852          * @type Object
8853          */
8854         this.o = o;
8855         var s = this.meta, Record = this.recordType,
8856             f = Record.prototype.fields, fi = f.items, fl = f.length;
8857
8858 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8859         if (!this.ef) {
8860             if(s.totalProperty) {
8861                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8862                 }
8863                 if(s.successProperty) {
8864                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8865                 }
8866                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8867                 if (s.id) {
8868                         var g = this.getJsonAccessor(s.id);
8869                         this.getId = function(rec) {
8870                                 var r = g(rec);
8871                                 return (r === undefined || r === "") ? null : r;
8872                         };
8873                 } else {
8874                         this.getId = function(){return null;};
8875                 }
8876             this.ef = [];
8877             for(var jj = 0; jj < fl; jj++){
8878                 f = fi[jj];
8879                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8880                 this.ef[jj] = this.getJsonAccessor(map);
8881             }
8882         }
8883
8884         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8885         if(s.totalProperty){
8886             var vt = parseInt(this.getTotal(o), 10);
8887             if(!isNaN(vt)){
8888                 totalRecords = vt;
8889             }
8890         }
8891         if(s.successProperty){
8892             var vs = this.getSuccess(o);
8893             if(vs === false || vs === 'false'){
8894                 success = false;
8895             }
8896         }
8897         var records = [];
8898             for(var i = 0; i < c; i++){
8899                     var n = root[i];
8900                 var values = {};
8901                 var id = this.getId(n);
8902                 for(var j = 0; j < fl; j++){
8903                     f = fi[j];
8904                 var v = this.ef[j](n);
8905                 if (!f.convert) {
8906                     Roo.log('missing convert for ' + f.name);
8907                     Roo.log(f);
8908                     continue;
8909                 }
8910                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8911                 }
8912                 var record = new Record(values, id);
8913                 record.json = n;
8914                 records[i] = record;
8915             }
8916             return {
8917             raw : o,
8918                 success : success,
8919                 records : records,
8920                 totalRecords : totalRecords
8921             };
8922     }
8923 });/*
8924  * Based on:
8925  * Ext JS Library 1.1.1
8926  * Copyright(c) 2006-2007, Ext JS, LLC.
8927  *
8928  * Originally Released Under LGPL - original licence link has changed is not relivant.
8929  *
8930  * Fork - LGPL
8931  * <script type="text/javascript">
8932  */
8933
8934 /**
8935  * @class Roo.data.ArrayReader
8936  * @extends Roo.data.DataReader
8937  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8938  * Each element of that Array represents a row of data fields. The
8939  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8940  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8941  * <p>
8942  * Example code:.
8943  * <pre><code>
8944 var RecordDef = Roo.data.Record.create([
8945     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8946     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8947 ]);
8948 var myReader = new Roo.data.ArrayReader({
8949     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8950 }, RecordDef);
8951 </code></pre>
8952  * <p>
8953  * This would consume an Array like this:
8954  * <pre><code>
8955 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8956   </code></pre>
8957  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8958  * @constructor
8959  * Create a new JsonReader
8960  * @param {Object} meta Metadata configuration options.
8961  * @param {Object} recordType Either an Array of field definition objects
8962  * as specified to {@link Roo.data.Record#create},
8963  * or an {@link Roo.data.Record} object
8964  * created using {@link Roo.data.Record#create}.
8965  */
8966 Roo.data.ArrayReader = function(meta, recordType){
8967     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8968 };
8969
8970 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8971     /**
8972      * Create a data block containing Roo.data.Records from an XML document.
8973      * @param {Object} o An Array of row objects which represents the dataset.
8974      * @return {Object} data A data block which is used by an Roo.data.Store object as
8975      * a cache of Roo.data.Records.
8976      */
8977     readRecords : function(o){
8978         var sid = this.meta ? this.meta.id : null;
8979         var recordType = this.recordType, fields = recordType.prototype.fields;
8980         var records = [];
8981         var root = o;
8982             for(var i = 0; i < root.length; i++){
8983                     var n = root[i];
8984                 var values = {};
8985                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8986                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8987                 var f = fields.items[j];
8988                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8989                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8990                 v = f.convert(v);
8991                 values[f.name] = v;
8992             }
8993                 var record = new recordType(values, id);
8994                 record.json = n;
8995                 records[records.length] = record;
8996             }
8997             return {
8998                 records : records,
8999                 totalRecords : records.length
9000             };
9001     }
9002 });/*
9003  * - LGPL
9004  * * 
9005  */
9006
9007 /**
9008  * @class Roo.bootstrap.ComboBox
9009  * @extends Roo.bootstrap.TriggerField
9010  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9011  * @cfg {Boolean} append (true|false) default false
9012  * @constructor
9013  * Create a new ComboBox.
9014  * @param {Object} config Configuration options
9015  */
9016 Roo.bootstrap.ComboBox = function(config){
9017     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9018     this.addEvents({
9019         /**
9020          * @event expand
9021          * Fires when the dropdown list is expanded
9022              * @param {Roo.bootstrap.ComboBox} combo This combo box
9023              */
9024         'expand' : true,
9025         /**
9026          * @event collapse
9027          * Fires when the dropdown list is collapsed
9028              * @param {Roo.bootstrap.ComboBox} combo This combo box
9029              */
9030         'collapse' : true,
9031         /**
9032          * @event beforeselect
9033          * Fires before a list item is selected. Return false to cancel the selection.
9034              * @param {Roo.bootstrap.ComboBox} combo This combo box
9035              * @param {Roo.data.Record} record The data record returned from the underlying store
9036              * @param {Number} index The index of the selected item in the dropdown list
9037              */
9038         'beforeselect' : true,
9039         /**
9040          * @event select
9041          * Fires when a list item is selected
9042              * @param {Roo.bootstrap.ComboBox} combo This combo box
9043              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9044              * @param {Number} index The index of the selected item in the dropdown list
9045              */
9046         'select' : true,
9047         /**
9048          * @event beforequery
9049          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9050          * The event object passed has these properties:
9051              * @param {Roo.bootstrap.ComboBox} combo This combo box
9052              * @param {String} query The query
9053              * @param {Boolean} forceAll true to force "all" query
9054              * @param {Boolean} cancel true to cancel the query
9055              * @param {Object} e The query event object
9056              */
9057         'beforequery': true,
9058          /**
9059          * @event add
9060          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9061              * @param {Roo.bootstrap.ComboBox} combo This combo box
9062              */
9063         'add' : true,
9064         /**
9065          * @event edit
9066          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9067              * @param {Roo.bootstrap.ComboBox} combo This combo box
9068              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9069              */
9070         'edit' : true,
9071         /**
9072          * @event remove
9073          * Fires when the remove value from the combobox array
9074              * @param {Roo.bootstrap.ComboBox} combo This combo box
9075              */
9076         'remove' : true
9077         
9078     });
9079     
9080     
9081     this.selectedIndex = -1;
9082     if(this.mode == 'local'){
9083         if(config.queryDelay === undefined){
9084             this.queryDelay = 10;
9085         }
9086         if(config.minChars === undefined){
9087             this.minChars = 0;
9088         }
9089     }
9090 };
9091
9092 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9093      
9094     /**
9095      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9096      * rendering into an Roo.Editor, defaults to false)
9097      */
9098     /**
9099      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9100      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9101      */
9102     /**
9103      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9104      */
9105     /**
9106      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9107      * the dropdown list (defaults to undefined, with no header element)
9108      */
9109
9110      /**
9111      * @cfg {String/Roo.Template} tpl The template to use to render the output
9112      */
9113      
9114      /**
9115      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9116      */
9117     listWidth: undefined,
9118     /**
9119      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9120      * mode = 'remote' or 'text' if mode = 'local')
9121      */
9122     displayField: undefined,
9123     /**
9124      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9125      * mode = 'remote' or 'value' if mode = 'local'). 
9126      * Note: use of a valueField requires the user make a selection
9127      * in order for a value to be mapped.
9128      */
9129     valueField: undefined,
9130     
9131     
9132     /**
9133      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9134      * field's data value (defaults to the underlying DOM element's name)
9135      */
9136     hiddenName: undefined,
9137     /**
9138      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9139      */
9140     listClass: '',
9141     /**
9142      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9143      */
9144     selectedClass: 'active',
9145     
9146     /**
9147      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9148      */
9149     shadow:'sides',
9150     /**
9151      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9152      * anchor positions (defaults to 'tl-bl')
9153      */
9154     listAlign: 'tl-bl?',
9155     /**
9156      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9157      */
9158     maxHeight: 300,
9159     /**
9160      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9161      * query specified by the allQuery config option (defaults to 'query')
9162      */
9163     triggerAction: 'query',
9164     /**
9165      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9166      * (defaults to 4, does not apply if editable = false)
9167      */
9168     minChars : 4,
9169     /**
9170      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9171      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9172      */
9173     typeAhead: false,
9174     /**
9175      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9176      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9177      */
9178     queryDelay: 500,
9179     /**
9180      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9181      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9182      */
9183     pageSize: 0,
9184     /**
9185      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9186      * when editable = true (defaults to false)
9187      */
9188     selectOnFocus:false,
9189     /**
9190      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9191      */
9192     queryParam: 'query',
9193     /**
9194      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9195      * when mode = 'remote' (defaults to 'Loading...')
9196      */
9197     loadingText: 'Loading...',
9198     /**
9199      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9200      */
9201     resizable: false,
9202     /**
9203      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9204      */
9205     handleHeight : 8,
9206     /**
9207      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9208      * traditional select (defaults to true)
9209      */
9210     editable: true,
9211     /**
9212      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9213      */
9214     allQuery: '',
9215     /**
9216      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9217      */
9218     mode: 'remote',
9219     /**
9220      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9221      * listWidth has a higher value)
9222      */
9223     minListWidth : 70,
9224     /**
9225      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9226      * allow the user to set arbitrary text into the field (defaults to false)
9227      */
9228     forceSelection:false,
9229     /**
9230      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9231      * if typeAhead = true (defaults to 250)
9232      */
9233     typeAheadDelay : 250,
9234     /**
9235      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9236      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9237      */
9238     valueNotFoundText : undefined,
9239     /**
9240      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9241      */
9242     blockFocus : false,
9243     
9244     /**
9245      * @cfg {Boolean} disableClear Disable showing of clear button.
9246      */
9247     disableClear : false,
9248     /**
9249      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9250      */
9251     alwaysQuery : false,
9252     
9253     /**
9254      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9255      */
9256     multiple : false,
9257     
9258     //private
9259     addicon : false,
9260     editicon: false,
9261     
9262     page: 0,
9263     hasQuery: false,
9264     append: false,
9265     loadNext: false,
9266     item: [],
9267     
9268     // element that contains real text value.. (when hidden is used..)
9269      
9270     // private
9271     initEvents: function(){
9272         
9273         if (!this.store) {
9274             throw "can not find store for combo";
9275         }
9276         this.store = Roo.factory(this.store, Roo.data);
9277         
9278         
9279         
9280         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9281         
9282         
9283         if(this.hiddenName){
9284             
9285             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9286             
9287             this.hiddenField.dom.value =
9288                 this.hiddenValue !== undefined ? this.hiddenValue :
9289                 this.value !== undefined ? this.value : '';
9290
9291             // prevent input submission
9292             this.el.dom.removeAttribute('name');
9293             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9294              
9295              
9296         }
9297         //if(Roo.isGecko){
9298         //    this.el.dom.setAttribute('autocomplete', 'off');
9299         //}
9300
9301         var cls = 'x-combo-list';
9302         this.list = this.el.select('ul.dropdown-menu',true).first();
9303
9304         //this.list = new Roo.Layer({
9305         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9306         //});
9307         
9308         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9309         this.list.setWidth(lw);
9310         
9311         this.list.on('mouseover', this.onViewOver, this);
9312         this.list.on('mousemove', this.onViewMove, this);
9313         
9314         this.list.on('scroll', this.onViewScroll, this);
9315         
9316         /*
9317         this.list.swallowEvent('mousewheel');
9318         this.assetHeight = 0;
9319
9320         if(this.title){
9321             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9322             this.assetHeight += this.header.getHeight();
9323         }
9324
9325         this.innerList = this.list.createChild({cls:cls+'-inner'});
9326         this.innerList.on('mouseover', this.onViewOver, this);
9327         this.innerList.on('mousemove', this.onViewMove, this);
9328         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9329         
9330         if(this.allowBlank && !this.pageSize && !this.disableClear){
9331             this.footer = this.list.createChild({cls:cls+'-ft'});
9332             this.pageTb = new Roo.Toolbar(this.footer);
9333            
9334         }
9335         if(this.pageSize){
9336             this.footer = this.list.createChild({cls:cls+'-ft'});
9337             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9338                     {pageSize: this.pageSize});
9339             
9340         }
9341         
9342         if (this.pageTb && this.allowBlank && !this.disableClear) {
9343             var _this = this;
9344             this.pageTb.add(new Roo.Toolbar.Fill(), {
9345                 cls: 'x-btn-icon x-btn-clear',
9346                 text: '&#160;',
9347                 handler: function()
9348                 {
9349                     _this.collapse();
9350                     _this.clearValue();
9351                     _this.onSelect(false, -1);
9352                 }
9353             });
9354         }
9355         if (this.footer) {
9356             this.assetHeight += this.footer.getHeight();
9357         }
9358         */
9359             
9360         if(!this.tpl){
9361             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9362         }
9363
9364         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9365             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9366         });
9367         //this.view.wrapEl.setDisplayed(false);
9368         this.view.on('click', this.onViewClick, this);
9369         
9370         
9371         
9372         this.store.on('beforeload', this.onBeforeLoad, this);
9373         this.store.on('load', this.onLoad, this);
9374         this.store.on('loadexception', this.onLoadException, this);
9375         /*
9376         if(this.resizable){
9377             this.resizer = new Roo.Resizable(this.list,  {
9378                pinned:true, handles:'se'
9379             });
9380             this.resizer.on('resize', function(r, w, h){
9381                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9382                 this.listWidth = w;
9383                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9384                 this.restrictHeight();
9385             }, this);
9386             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9387         }
9388         */
9389         if(!this.editable){
9390             this.editable = true;
9391             this.setEditable(false);
9392         }
9393         
9394         /*
9395         
9396         if (typeof(this.events.add.listeners) != 'undefined') {
9397             
9398             this.addicon = this.wrap.createChild(
9399                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9400        
9401             this.addicon.on('click', function(e) {
9402                 this.fireEvent('add', this);
9403             }, this);
9404         }
9405         if (typeof(this.events.edit.listeners) != 'undefined') {
9406             
9407             this.editicon = this.wrap.createChild(
9408                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9409             if (this.addicon) {
9410                 this.editicon.setStyle('margin-left', '40px');
9411             }
9412             this.editicon.on('click', function(e) {
9413                 
9414                 // we fire even  if inothing is selected..
9415                 this.fireEvent('edit', this, this.lastData );
9416                 
9417             }, this);
9418         }
9419         */
9420         
9421         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9422             "up" : function(e){
9423                 this.inKeyMode = true;
9424                 this.selectPrev();
9425             },
9426
9427             "down" : function(e){
9428                 if(!this.isExpanded()){
9429                     this.onTriggerClick();
9430                 }else{
9431                     this.inKeyMode = true;
9432                     this.selectNext();
9433                 }
9434             },
9435
9436             "enter" : function(e){
9437                 this.onViewClick();
9438                 //return true;
9439             },
9440
9441             "esc" : function(e){
9442                 this.collapse();
9443             },
9444
9445             "tab" : function(e){
9446                 this.collapse();
9447                 
9448                 if(this.fireEvent("specialkey", this, e)){
9449                     this.onViewClick(false);
9450                 }
9451                 
9452                 return true;
9453             },
9454
9455             scope : this,
9456
9457             doRelay : function(foo, bar, hname){
9458                 if(hname == 'down' || this.scope.isExpanded()){
9459                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9460                 }
9461                 return true;
9462             },
9463
9464             forceKeyDown: true
9465         });
9466         
9467         
9468         this.queryDelay = Math.max(this.queryDelay || 10,
9469                 this.mode == 'local' ? 10 : 250);
9470         
9471         
9472         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9473         
9474         if(this.typeAhead){
9475             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9476         }
9477         if(this.editable !== false){
9478             this.inputEl().on("keyup", this.onKeyUp, this);
9479         }
9480         if(this.forceSelection){
9481             this.on('blur', this.doForce, this);
9482         }
9483         
9484         if(this.multiple){
9485             this.choices = this.el.select('ul.select2-choices', true).first();
9486             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9487         }
9488     },
9489
9490     onDestroy : function(){
9491         if(this.view){
9492             this.view.setStore(null);
9493             this.view.el.removeAllListeners();
9494             this.view.el.remove();
9495             this.view.purgeListeners();
9496         }
9497         if(this.list){
9498             this.list.dom.innerHTML  = '';
9499         }
9500         if(this.store){
9501             this.store.un('beforeload', this.onBeforeLoad, this);
9502             this.store.un('load', this.onLoad, this);
9503             this.store.un('loadexception', this.onLoadException, this);
9504         }
9505         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9506     },
9507
9508     // private
9509     fireKey : function(e){
9510         if(e.isNavKeyPress() && !this.list.isVisible()){
9511             this.fireEvent("specialkey", this, e);
9512         }
9513     },
9514
9515     // private
9516     onResize: function(w, h){
9517 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9518 //        
9519 //        if(typeof w != 'number'){
9520 //            // we do not handle it!?!?
9521 //            return;
9522 //        }
9523 //        var tw = this.trigger.getWidth();
9524 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9525 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9526 //        var x = w - tw;
9527 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9528 //            
9529 //        //this.trigger.setStyle('left', x+'px');
9530 //        
9531 //        if(this.list && this.listWidth === undefined){
9532 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9533 //            this.list.setWidth(lw);
9534 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9535 //        }
9536         
9537     
9538         
9539     },
9540
9541     /**
9542      * Allow or prevent the user from directly editing the field text.  If false is passed,
9543      * the user will only be able to select from the items defined in the dropdown list.  This method
9544      * is the runtime equivalent of setting the 'editable' config option at config time.
9545      * @param {Boolean} value True to allow the user to directly edit the field text
9546      */
9547     setEditable : function(value){
9548         if(value == this.editable){
9549             return;
9550         }
9551         this.editable = value;
9552         if(!value){
9553             this.inputEl().dom.setAttribute('readOnly', true);
9554             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9555             this.inputEl().addClass('x-combo-noedit');
9556         }else{
9557             this.inputEl().dom.setAttribute('readOnly', false);
9558             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9559             this.inputEl().removeClass('x-combo-noedit');
9560         }
9561     },
9562
9563     // private
9564     
9565     onBeforeLoad : function(combo,opts){
9566         if(!this.hasFocus){
9567             return;
9568         }
9569          if (!opts.add) {
9570             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9571          }
9572         this.restrictHeight();
9573         this.selectedIndex = -1;
9574     },
9575
9576     // private
9577     onLoad : function(){
9578         
9579         this.hasQuery = false;
9580         
9581         if(!this.hasFocus){
9582             return;
9583         }
9584         
9585         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9586             this.loading.hide();
9587         }
9588         
9589         if(this.store.getCount() > 0){
9590             this.expand();
9591             this.restrictHeight();
9592             if(this.lastQuery == this.allQuery){
9593                 if(this.editable){
9594                     this.inputEl().dom.select();
9595                 }
9596                 if(!this.selectByValue(this.value, true)){
9597                     this.select(0, true);
9598                 }
9599             }else{
9600                 this.selectNext();
9601                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9602                     this.taTask.delay(this.typeAheadDelay);
9603                 }
9604             }
9605         }else{
9606             this.onEmptyResults();
9607         }
9608         
9609         //this.el.focus();
9610     },
9611     // private
9612     onLoadException : function()
9613     {
9614         this.hasQuery = false;
9615         
9616         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9617             this.loading.hide();
9618         }
9619         
9620         this.collapse();
9621         Roo.log(this.store.reader.jsonData);
9622         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9623             // fixme
9624             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9625         }
9626         
9627         
9628     },
9629     // private
9630     onTypeAhead : function(){
9631         if(this.store.getCount() > 0){
9632             var r = this.store.getAt(0);
9633             var newValue = r.data[this.displayField];
9634             var len = newValue.length;
9635             var selStart = this.getRawValue().length;
9636             
9637             if(selStart != len){
9638                 this.setRawValue(newValue);
9639                 this.selectText(selStart, newValue.length);
9640             }
9641         }
9642     },
9643
9644     // private
9645     onSelect : function(record, index){
9646         
9647         if(this.fireEvent('beforeselect', this, record, index) !== false){
9648         
9649             this.setFromData(index > -1 ? record.data : false);
9650             
9651             this.collapse();
9652             this.fireEvent('select', this, record, index);
9653         }
9654     },
9655
9656     /**
9657      * Returns the currently selected field value or empty string if no value is set.
9658      * @return {String} value The selected value
9659      */
9660     getValue : function(){
9661         
9662         if(this.multiple){
9663             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9664         }
9665         
9666         if(this.valueField){
9667             return typeof this.value != 'undefined' ? this.value : '';
9668         }else{
9669             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9670         }
9671     },
9672
9673     /**
9674      * Clears any text/value currently set in the field
9675      */
9676     clearValue : function(){
9677         if(this.hiddenField){
9678             this.hiddenField.dom.value = '';
9679         }
9680         this.value = '';
9681         this.setRawValue('');
9682         this.lastSelectionText = '';
9683         
9684     },
9685
9686     /**
9687      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9688      * will be displayed in the field.  If the value does not match the data value of an existing item,
9689      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9690      * Otherwise the field will be blank (although the value will still be set).
9691      * @param {String} value The value to match
9692      */
9693     setValue : function(v){
9694         if(this.multiple){
9695             this.syncValue();
9696             return;
9697         }
9698         
9699         var text = v;
9700         if(this.valueField){
9701             var r = this.findRecord(this.valueField, v);
9702             if(r){
9703                 text = r.data[this.displayField];
9704             }else if(this.valueNotFoundText !== undefined){
9705                 text = this.valueNotFoundText;
9706             }
9707         }
9708         this.lastSelectionText = text;
9709         if(this.hiddenField){
9710             this.hiddenField.dom.value = v;
9711         }
9712         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9713         this.value = v;
9714     },
9715     /**
9716      * @property {Object} the last set data for the element
9717      */
9718     
9719     lastData : false,
9720     /**
9721      * Sets the value of the field based on a object which is related to the record format for the store.
9722      * @param {Object} value the value to set as. or false on reset?
9723      */
9724     setFromData : function(o){
9725         
9726         if(this.multiple){
9727             this.addItem(o);
9728             return;
9729         }
9730             
9731         var dv = ''; // display value
9732         var vv = ''; // value value..
9733         this.lastData = o;
9734         if (this.displayField) {
9735             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9736         } else {
9737             // this is an error condition!!!
9738             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9739         }
9740         
9741         if(this.valueField){
9742             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9743         }
9744         
9745         if(this.hiddenField){
9746             this.hiddenField.dom.value = vv;
9747             
9748             this.lastSelectionText = dv;
9749             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9750             this.value = vv;
9751             return;
9752         }
9753         // no hidden field.. - we store the value in 'value', but still display
9754         // display field!!!!
9755         this.lastSelectionText = dv;
9756         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9757         this.value = vv;
9758         
9759         
9760     },
9761     // private
9762     reset : function(){
9763         // overridden so that last data is reset..
9764         this.setValue(this.originalValue);
9765         this.clearInvalid();
9766         this.lastData = false;
9767         if (this.view) {
9768             this.view.clearSelections();
9769         }
9770     },
9771     // private
9772     findRecord : function(prop, value){
9773         var record;
9774         if(this.store.getCount() > 0){
9775             this.store.each(function(r){
9776                 if(r.data[prop] == value){
9777                     record = r;
9778                     return false;
9779                 }
9780                 return true;
9781             });
9782         }
9783         return record;
9784     },
9785     
9786     getName: function()
9787     {
9788         // returns hidden if it's set..
9789         if (!this.rendered) {return ''};
9790         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9791         
9792     },
9793     // private
9794     onViewMove : function(e, t){
9795         this.inKeyMode = false;
9796     },
9797
9798     // private
9799     onViewOver : function(e, t){
9800         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9801             return;
9802         }
9803         var item = this.view.findItemFromChild(t);
9804         if(item){
9805             var index = this.view.indexOf(item);
9806             this.select(index, false);
9807         }
9808     },
9809
9810     // private
9811     onViewClick : function(doFocus)
9812     {
9813         var index = this.view.getSelectedIndexes()[0];
9814         var r = this.store.getAt(index);
9815         if(r){
9816             this.onSelect(r, index);
9817         }
9818         if(doFocus !== false && !this.blockFocus){
9819             this.inputEl().focus();
9820         }
9821     },
9822
9823     // private
9824     restrictHeight : function(){
9825         //this.innerList.dom.style.height = '';
9826         //var inner = this.innerList.dom;
9827         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9828         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9829         //this.list.beginUpdate();
9830         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9831         this.list.alignTo(this.inputEl(), this.listAlign);
9832         //this.list.endUpdate();
9833     },
9834
9835     // private
9836     onEmptyResults : function(){
9837         this.collapse();
9838     },
9839
9840     /**
9841      * Returns true if the dropdown list is expanded, else false.
9842      */
9843     isExpanded : function(){
9844         return this.list.isVisible();
9845     },
9846
9847     /**
9848      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9849      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9850      * @param {String} value The data value of the item to select
9851      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9852      * selected item if it is not currently in view (defaults to true)
9853      * @return {Boolean} True if the value matched an item in the list, else false
9854      */
9855     selectByValue : function(v, scrollIntoView){
9856         if(v !== undefined && v !== null){
9857             var r = this.findRecord(this.valueField || this.displayField, v);
9858             if(r){
9859                 this.select(this.store.indexOf(r), scrollIntoView);
9860                 return true;
9861             }
9862         }
9863         return false;
9864     },
9865
9866     /**
9867      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9868      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9869      * @param {Number} index The zero-based index of the list item to select
9870      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9871      * selected item if it is not currently in view (defaults to true)
9872      */
9873     select : function(index, scrollIntoView){
9874         this.selectedIndex = index;
9875         this.view.select(index);
9876         if(scrollIntoView !== false){
9877             var el = this.view.getNode(index);
9878             if(el){
9879                 //this.innerList.scrollChildIntoView(el, false);
9880                 
9881             }
9882         }
9883     },
9884
9885     // private
9886     selectNext : function(){
9887         var ct = this.store.getCount();
9888         if(ct > 0){
9889             if(this.selectedIndex == -1){
9890                 this.select(0);
9891             }else if(this.selectedIndex < ct-1){
9892                 this.select(this.selectedIndex+1);
9893             }
9894         }
9895     },
9896
9897     // private
9898     selectPrev : function(){
9899         var ct = this.store.getCount();
9900         if(ct > 0){
9901             if(this.selectedIndex == -1){
9902                 this.select(0);
9903             }else if(this.selectedIndex != 0){
9904                 this.select(this.selectedIndex-1);
9905             }
9906         }
9907     },
9908
9909     // private
9910     onKeyUp : function(e){
9911         if(this.editable !== false && !e.isSpecialKey()){
9912             this.lastKey = e.getKey();
9913             this.dqTask.delay(this.queryDelay);
9914         }
9915     },
9916
9917     // private
9918     validateBlur : function(){
9919         return !this.list || !this.list.isVisible();   
9920     },
9921
9922     // private
9923     initQuery : function(){
9924         this.doQuery(this.getRawValue());
9925     },
9926
9927     // private
9928     doForce : function(){
9929         if(this.el.dom.value.length > 0){
9930             this.el.dom.value =
9931                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9932              
9933         }
9934     },
9935
9936     /**
9937      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9938      * query allowing the query action to be canceled if needed.
9939      * @param {String} query The SQL query to execute
9940      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9941      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9942      * saved in the current store (defaults to false)
9943      */
9944     doQuery : function(q, forceAll){
9945         
9946         if(q === undefined || q === null){
9947             q = '';
9948         }
9949         var qe = {
9950             query: q,
9951             forceAll: forceAll,
9952             combo: this,
9953             cancel:false
9954         };
9955         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9956             return false;
9957         }
9958         q = qe.query;
9959         
9960         forceAll = qe.forceAll;
9961         if(forceAll === true || (q.length >= this.minChars)){
9962             
9963             this.hasQuery = true;
9964             
9965             if(this.lastQuery != q || this.alwaysQuery){
9966                 this.lastQuery = q;
9967                 if(this.mode == 'local'){
9968                     this.selectedIndex = -1;
9969                     if(forceAll){
9970                         this.store.clearFilter();
9971                     }else{
9972                         this.store.filter(this.displayField, q);
9973                     }
9974                     this.onLoad();
9975                 }else{
9976                     this.store.baseParams[this.queryParam] = q;
9977                     
9978                     var options = {params : this.getParams(q)};
9979                     
9980                     if(this.loadNext){
9981                         options.add = true;
9982                         options.params.start = this.page * this.pageSize;
9983                     }
9984                     
9985                     this.store.load(options);
9986                     this.expand();
9987                 }
9988             }else{
9989                 this.selectedIndex = -1;
9990                 this.onLoad();   
9991             }
9992         }
9993         
9994         this.loadNext = false;
9995     },
9996
9997     // private
9998     getParams : function(q){
9999         var p = {};
10000         //p[this.queryParam] = q;
10001         
10002         if(this.pageSize){
10003             p.start = 0;
10004             p.limit = this.pageSize;
10005         }
10006         return p;
10007     },
10008
10009     /**
10010      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10011      */
10012     collapse : function(){
10013         if(!this.isExpanded()){
10014             return;
10015         }
10016         
10017         this.list.hide();
10018         Roo.get(document).un('mousedown', this.collapseIf, this);
10019         Roo.get(document).un('mousewheel', this.collapseIf, this);
10020         if (!this.editable) {
10021             Roo.get(document).un('keydown', this.listKeyPress, this);
10022         }
10023         this.fireEvent('collapse', this);
10024     },
10025
10026     // private
10027     collapseIf : function(e){
10028         var in_combo  = e.within(this.el);
10029         var in_list =  e.within(this.list);
10030         
10031         if (in_combo || in_list) {
10032             //e.stopPropagation();
10033             return;
10034         }
10035
10036         this.collapse();
10037         
10038     },
10039
10040     /**
10041      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10042      */
10043     expand : function(){
10044        
10045         if(this.isExpanded() || !this.hasFocus){
10046             return;
10047         }
10048          Roo.log('expand');
10049         this.list.alignTo(this.inputEl(), this.listAlign);
10050         this.list.show();
10051         Roo.get(document).on('mousedown', this.collapseIf, this);
10052         Roo.get(document).on('mousewheel', this.collapseIf, this);
10053         if (!this.editable) {
10054             Roo.get(document).on('keydown', this.listKeyPress, this);
10055         }
10056         
10057         this.fireEvent('expand', this);
10058     },
10059
10060     // private
10061     // Implements the default empty TriggerField.onTriggerClick function
10062     onTriggerClick : function()
10063     {
10064         Roo.log('trigger click');
10065         
10066         if(this.disabled){
10067             return;
10068         }
10069         
10070         this.page = 0;
10071         this.loadNext = false;
10072         
10073         if(this.isExpanded()){
10074             this.collapse();
10075             if (!this.blockFocus) {
10076                 this.inputEl().focus();
10077             }
10078             
10079         }else {
10080             this.hasFocus = true;
10081             if(this.triggerAction == 'all') {
10082                 this.doQuery(this.allQuery, true);
10083             } else {
10084                 this.doQuery(this.getRawValue());
10085             }
10086             if (!this.blockFocus) {
10087                 this.inputEl().focus();
10088             }
10089         }
10090     },
10091     listKeyPress : function(e)
10092     {
10093         //Roo.log('listkeypress');
10094         // scroll to first matching element based on key pres..
10095         if (e.isSpecialKey()) {
10096             return false;
10097         }
10098         var k = String.fromCharCode(e.getKey()).toUpperCase();
10099         //Roo.log(k);
10100         var match  = false;
10101         var csel = this.view.getSelectedNodes();
10102         var cselitem = false;
10103         if (csel.length) {
10104             var ix = this.view.indexOf(csel[0]);
10105             cselitem  = this.store.getAt(ix);
10106             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10107                 cselitem = false;
10108             }
10109             
10110         }
10111         
10112         this.store.each(function(v) { 
10113             if (cselitem) {
10114                 // start at existing selection.
10115                 if (cselitem.id == v.id) {
10116                     cselitem = false;
10117                 }
10118                 return true;
10119             }
10120                 
10121             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10122                 match = this.store.indexOf(v);
10123                 return false;
10124             }
10125             return true;
10126         }, this);
10127         
10128         if (match === false) {
10129             return true; // no more action?
10130         }
10131         // scroll to?
10132         this.view.select(match);
10133         var sn = Roo.get(this.view.getSelectedNodes()[0])
10134         //sn.scrollIntoView(sn.dom.parentNode, false);
10135     },
10136     
10137     onViewScroll : function(e, t){
10138         
10139         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10140             return;
10141         }
10142         
10143         this.hasQuery = true;
10144         
10145         this.loading = this.list.select('.loading', true).first();
10146         
10147         if(this.loading === null){
10148             this.list.createChild({
10149                 tag: 'div',
10150                 cls: 'loading select2-more-results select2-active',
10151                 html: 'Loading more results...'
10152             })
10153             
10154             this.loading = this.list.select('.loading', true).first();
10155             
10156             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10157             
10158             this.loading.hide();
10159         }
10160         
10161         this.loading.show();
10162         
10163         var _combo = this;
10164         
10165         this.page++;
10166         this.loadNext = true;
10167         
10168         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10169         
10170         return;
10171     },
10172     
10173     addItem : function(o)
10174     {   
10175         var dv = ''; // display value
10176         
10177         if (this.displayField) {
10178             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10179         } else {
10180             // this is an error condition!!!
10181             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10182         }
10183         
10184         if(!dv.length){
10185             return;
10186         }
10187         
10188         var choice = this.choices.createChild({
10189             tag: 'li',
10190             cls: 'select2-search-choice',
10191             cn: [
10192                 {
10193                     tag: 'div',
10194                     html: dv
10195                 },
10196                 {
10197                     tag: 'a',
10198                     href: '#',
10199                     cls: 'select2-search-choice-close',
10200                     tabindex: '-1'
10201                 }
10202             ]
10203             
10204         }, this.searchField);
10205         
10206         var close = choice.select('a.select2-search-choice-close', true).first()
10207         
10208         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10209         
10210         this.item.push(o);
10211         this.lastData = o;
10212         
10213         this.syncValue();
10214         
10215         this.inputEl().dom.value = '';
10216         
10217     },
10218     
10219     onRemoveItem : function(e, _self, o)
10220     {
10221         Roo.log('remove item');
10222         var index = this.item.indexOf(o.data) * 1;
10223         
10224         if( index < 0){
10225             Roo.log('not this item?!');
10226             return;
10227         }
10228         
10229         this.item.splice(index, 1);
10230         o.item.remove();
10231         
10232         this.syncValue();
10233         
10234         this.fireEvent('remove', this, e);
10235         
10236     },
10237     
10238     syncValue : function()
10239     {
10240         if(!this.item.length){
10241             this.clearValue();
10242             return;
10243         }
10244             
10245         var value = [];
10246         var _this = this;
10247         Roo.each(this.item, function(i){
10248             if(_this.valueField){
10249                 value.push(i[_this.valueField]);
10250                 return;
10251             }
10252
10253             value.push(i);
10254         });
10255
10256         this.value = value.join(',');
10257
10258         if(this.hiddenField){
10259             this.hiddenField.dom.value = this.value;
10260         }
10261     },
10262     
10263     clearItem : function()
10264     {
10265         if(!this.multiple){
10266             return;
10267         }
10268         
10269         this.item = [];
10270         
10271         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10272            c.remove();
10273         });
10274         
10275         this.syncValue();
10276     }
10277     
10278     
10279
10280     /** 
10281     * @cfg {Boolean} grow 
10282     * @hide 
10283     */
10284     /** 
10285     * @cfg {Number} growMin 
10286     * @hide 
10287     */
10288     /** 
10289     * @cfg {Number} growMax 
10290     * @hide 
10291     */
10292     /**
10293      * @hide
10294      * @method autoSize
10295      */
10296 });
10297 /*
10298  * Based on:
10299  * Ext JS Library 1.1.1
10300  * Copyright(c) 2006-2007, Ext JS, LLC.
10301  *
10302  * Originally Released Under LGPL - original licence link has changed is not relivant.
10303  *
10304  * Fork - LGPL
10305  * <script type="text/javascript">
10306  */
10307
10308 /**
10309  * @class Roo.View
10310  * @extends Roo.util.Observable
10311  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10312  * This class also supports single and multi selection modes. <br>
10313  * Create a data model bound view:
10314  <pre><code>
10315  var store = new Roo.data.Store(...);
10316
10317  var view = new Roo.View({
10318     el : "my-element",
10319     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10320  
10321     singleSelect: true,
10322     selectedClass: "ydataview-selected",
10323     store: store
10324  });
10325
10326  // listen for node click?
10327  view.on("click", function(vw, index, node, e){
10328  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10329  });
10330
10331  // load XML data
10332  dataModel.load("foobar.xml");
10333  </code></pre>
10334  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10335  * <br><br>
10336  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10337  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10338  * 
10339  * Note: old style constructor is still suported (container, template, config)
10340  * 
10341  * @constructor
10342  * Create a new View
10343  * @param {Object} config The config object
10344  * 
10345  */
10346 Roo.View = function(config, depreciated_tpl, depreciated_config){
10347     
10348     if (typeof(depreciated_tpl) == 'undefined') {
10349         // new way.. - universal constructor.
10350         Roo.apply(this, config);
10351         this.el  = Roo.get(this.el);
10352     } else {
10353         // old format..
10354         this.el  = Roo.get(config);
10355         this.tpl = depreciated_tpl;
10356         Roo.apply(this, depreciated_config);
10357     }
10358     this.wrapEl  = this.el.wrap().wrap();
10359     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10360     
10361     
10362     if(typeof(this.tpl) == "string"){
10363         this.tpl = new Roo.Template(this.tpl);
10364     } else {
10365         // support xtype ctors..
10366         this.tpl = new Roo.factory(this.tpl, Roo);
10367     }
10368     
10369     
10370     this.tpl.compile();
10371    
10372   
10373     
10374      
10375     /** @private */
10376     this.addEvents({
10377         /**
10378          * @event beforeclick
10379          * Fires before a click is processed. Returns false to cancel the default action.
10380          * @param {Roo.View} this
10381          * @param {Number} index The index of the target node
10382          * @param {HTMLElement} node The target node
10383          * @param {Roo.EventObject} e The raw event object
10384          */
10385             "beforeclick" : true,
10386         /**
10387          * @event click
10388          * Fires when a template node is clicked.
10389          * @param {Roo.View} this
10390          * @param {Number} index The index of the target node
10391          * @param {HTMLElement} node The target node
10392          * @param {Roo.EventObject} e The raw event object
10393          */
10394             "click" : true,
10395         /**
10396          * @event dblclick
10397          * Fires when a template node is double clicked.
10398          * @param {Roo.View} this
10399          * @param {Number} index The index of the target node
10400          * @param {HTMLElement} node The target node
10401          * @param {Roo.EventObject} e The raw event object
10402          */
10403             "dblclick" : true,
10404         /**
10405          * @event contextmenu
10406          * Fires when a template node is right clicked.
10407          * @param {Roo.View} this
10408          * @param {Number} index The index of the target node
10409          * @param {HTMLElement} node The target node
10410          * @param {Roo.EventObject} e The raw event object
10411          */
10412             "contextmenu" : true,
10413         /**
10414          * @event selectionchange
10415          * Fires when the selected nodes change.
10416          * @param {Roo.View} this
10417          * @param {Array} selections Array of the selected nodes
10418          */
10419             "selectionchange" : true,
10420     
10421         /**
10422          * @event beforeselect
10423          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10424          * @param {Roo.View} this
10425          * @param {HTMLElement} node The node to be selected
10426          * @param {Array} selections Array of currently selected nodes
10427          */
10428             "beforeselect" : true,
10429         /**
10430          * @event preparedata
10431          * Fires on every row to render, to allow you to change the data.
10432          * @param {Roo.View} this
10433          * @param {Object} data to be rendered (change this)
10434          */
10435           "preparedata" : true
10436           
10437           
10438         });
10439
10440
10441
10442     this.el.on({
10443         "click": this.onClick,
10444         "dblclick": this.onDblClick,
10445         "contextmenu": this.onContextMenu,
10446         scope:this
10447     });
10448
10449     this.selections = [];
10450     this.nodes = [];
10451     this.cmp = new Roo.CompositeElementLite([]);
10452     if(this.store){
10453         this.store = Roo.factory(this.store, Roo.data);
10454         this.setStore(this.store, true);
10455     }
10456     
10457     if ( this.footer && this.footer.xtype) {
10458            
10459          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10460         
10461         this.footer.dataSource = this.store
10462         this.footer.container = fctr;
10463         this.footer = Roo.factory(this.footer, Roo);
10464         fctr.insertFirst(this.el);
10465         
10466         // this is a bit insane - as the paging toolbar seems to detach the el..
10467 //        dom.parentNode.parentNode.parentNode
10468          // they get detached?
10469     }
10470     
10471     
10472     Roo.View.superclass.constructor.call(this);
10473     
10474     
10475 };
10476
10477 Roo.extend(Roo.View, Roo.util.Observable, {
10478     
10479      /**
10480      * @cfg {Roo.data.Store} store Data store to load data from.
10481      */
10482     store : false,
10483     
10484     /**
10485      * @cfg {String|Roo.Element} el The container element.
10486      */
10487     el : '',
10488     
10489     /**
10490      * @cfg {String|Roo.Template} tpl The template used by this View 
10491      */
10492     tpl : false,
10493     /**
10494      * @cfg {String} dataName the named area of the template to use as the data area
10495      *                          Works with domtemplates roo-name="name"
10496      */
10497     dataName: false,
10498     /**
10499      * @cfg {String} selectedClass The css class to add to selected nodes
10500      */
10501     selectedClass : "x-view-selected",
10502      /**
10503      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10504      */
10505     emptyText : "",
10506     
10507     /**
10508      * @cfg {String} text to display on mask (default Loading)
10509      */
10510     mask : false,
10511     /**
10512      * @cfg {Boolean} multiSelect Allow multiple selection
10513      */
10514     multiSelect : false,
10515     /**
10516      * @cfg {Boolean} singleSelect Allow single selection
10517      */
10518     singleSelect:  false,
10519     
10520     /**
10521      * @cfg {Boolean} toggleSelect - selecting 
10522      */
10523     toggleSelect : false,
10524     
10525     /**
10526      * Returns the element this view is bound to.
10527      * @return {Roo.Element}
10528      */
10529     getEl : function(){
10530         return this.wrapEl;
10531     },
10532     
10533     
10534
10535     /**
10536      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10537      */
10538     refresh : function(){
10539         Roo.log('refresh');
10540         var t = this.tpl;
10541         
10542         // if we are using something like 'domtemplate', then
10543         // the what gets used is:
10544         // t.applySubtemplate(NAME, data, wrapping data..)
10545         // the outer template then get' applied with
10546         //     the store 'extra data'
10547         // and the body get's added to the
10548         //      roo-name="data" node?
10549         //      <span class='roo-tpl-{name}'></span> ?????
10550         
10551         
10552         
10553         this.clearSelections();
10554         this.el.update("");
10555         var html = [];
10556         var records = this.store.getRange();
10557         if(records.length < 1) {
10558             
10559             // is this valid??  = should it render a template??
10560             
10561             this.el.update(this.emptyText);
10562             return;
10563         }
10564         var el = this.el;
10565         if (this.dataName) {
10566             this.el.update(t.apply(this.store.meta)); //????
10567             el = this.el.child('.roo-tpl-' + this.dataName);
10568         }
10569         
10570         for(var i = 0, len = records.length; i < len; i++){
10571             var data = this.prepareData(records[i].data, i, records[i]);
10572             this.fireEvent("preparedata", this, data, i, records[i]);
10573             html[html.length] = Roo.util.Format.trim(
10574                 this.dataName ?
10575                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10576                     t.apply(data)
10577             );
10578         }
10579         
10580         
10581         
10582         el.update(html.join(""));
10583         this.nodes = el.dom.childNodes;
10584         this.updateIndexes(0);
10585     },
10586     
10587
10588     /**
10589      * Function to override to reformat the data that is sent to
10590      * the template for each node.
10591      * DEPRICATED - use the preparedata event handler.
10592      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10593      * a JSON object for an UpdateManager bound view).
10594      */
10595     prepareData : function(data, index, record)
10596     {
10597         this.fireEvent("preparedata", this, data, index, record);
10598         return data;
10599     },
10600
10601     onUpdate : function(ds, record){
10602          Roo.log('on update');   
10603         this.clearSelections();
10604         var index = this.store.indexOf(record);
10605         var n = this.nodes[index];
10606         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10607         n.parentNode.removeChild(n);
10608         this.updateIndexes(index, index);
10609     },
10610
10611     
10612     
10613 // --------- FIXME     
10614     onAdd : function(ds, records, index)
10615     {
10616         Roo.log(['on Add', ds, records, index] );        
10617         this.clearSelections();
10618         if(this.nodes.length == 0){
10619             this.refresh();
10620             return;
10621         }
10622         var n = this.nodes[index];
10623         for(var i = 0, len = records.length; i < len; i++){
10624             var d = this.prepareData(records[i].data, i, records[i]);
10625             if(n){
10626                 this.tpl.insertBefore(n, d);
10627             }else{
10628                 
10629                 this.tpl.append(this.el, d);
10630             }
10631         }
10632         this.updateIndexes(index);
10633     },
10634
10635     onRemove : function(ds, record, index){
10636         Roo.log('onRemove');
10637         this.clearSelections();
10638         var el = this.dataName  ?
10639             this.el.child('.roo-tpl-' + this.dataName) :
10640             this.el; 
10641         
10642         el.dom.removeChild(this.nodes[index]);
10643         this.updateIndexes(index);
10644     },
10645
10646     /**
10647      * Refresh an individual node.
10648      * @param {Number} index
10649      */
10650     refreshNode : function(index){
10651         this.onUpdate(this.store, this.store.getAt(index));
10652     },
10653
10654     updateIndexes : function(startIndex, endIndex){
10655         var ns = this.nodes;
10656         startIndex = startIndex || 0;
10657         endIndex = endIndex || ns.length - 1;
10658         for(var i = startIndex; i <= endIndex; i++){
10659             ns[i].nodeIndex = i;
10660         }
10661     },
10662
10663     /**
10664      * Changes the data store this view uses and refresh the view.
10665      * @param {Store} store
10666      */
10667     setStore : function(store, initial){
10668         if(!initial && this.store){
10669             this.store.un("datachanged", this.refresh);
10670             this.store.un("add", this.onAdd);
10671             this.store.un("remove", this.onRemove);
10672             this.store.un("update", this.onUpdate);
10673             this.store.un("clear", this.refresh);
10674             this.store.un("beforeload", this.onBeforeLoad);
10675             this.store.un("load", this.onLoad);
10676             this.store.un("loadexception", this.onLoad);
10677         }
10678         if(store){
10679           
10680             store.on("datachanged", this.refresh, this);
10681             store.on("add", this.onAdd, this);
10682             store.on("remove", this.onRemove, this);
10683             store.on("update", this.onUpdate, this);
10684             store.on("clear", this.refresh, this);
10685             store.on("beforeload", this.onBeforeLoad, this);
10686             store.on("load", this.onLoad, this);
10687             store.on("loadexception", this.onLoad, this);
10688         }
10689         
10690         if(store){
10691             this.refresh();
10692         }
10693     },
10694     /**
10695      * onbeforeLoad - masks the loading area.
10696      *
10697      */
10698     onBeforeLoad : function(store,opts)
10699     {
10700          Roo.log('onBeforeLoad');   
10701         if (!opts.add) {
10702             this.el.update("");
10703         }
10704         this.el.mask(this.mask ? this.mask : "Loading" ); 
10705     },
10706     onLoad : function ()
10707     {
10708         this.el.unmask();
10709     },
10710     
10711
10712     /**
10713      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10714      * @param {HTMLElement} node
10715      * @return {HTMLElement} The template node
10716      */
10717     findItemFromChild : function(node){
10718         var el = this.dataName  ?
10719             this.el.child('.roo-tpl-' + this.dataName,true) :
10720             this.el.dom; 
10721         
10722         if(!node || node.parentNode == el){
10723                     return node;
10724             }
10725             var p = node.parentNode;
10726             while(p && p != el){
10727             if(p.parentNode == el){
10728                 return p;
10729             }
10730             p = p.parentNode;
10731         }
10732             return null;
10733     },
10734
10735     /** @ignore */
10736     onClick : function(e){
10737         var item = this.findItemFromChild(e.getTarget());
10738         if(item){
10739             var index = this.indexOf(item);
10740             if(this.onItemClick(item, index, e) !== false){
10741                 this.fireEvent("click", this, index, item, e);
10742             }
10743         }else{
10744             this.clearSelections();
10745         }
10746     },
10747
10748     /** @ignore */
10749     onContextMenu : function(e){
10750         var item = this.findItemFromChild(e.getTarget());
10751         if(item){
10752             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10753         }
10754     },
10755
10756     /** @ignore */
10757     onDblClick : function(e){
10758         var item = this.findItemFromChild(e.getTarget());
10759         if(item){
10760             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10761         }
10762     },
10763
10764     onItemClick : function(item, index, e)
10765     {
10766         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10767             return false;
10768         }
10769         if (this.toggleSelect) {
10770             var m = this.isSelected(item) ? 'unselect' : 'select';
10771             Roo.log(m);
10772             var _t = this;
10773             _t[m](item, true, false);
10774             return true;
10775         }
10776         if(this.multiSelect || this.singleSelect){
10777             if(this.multiSelect && e.shiftKey && this.lastSelection){
10778                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10779             }else{
10780                 this.select(item, this.multiSelect && e.ctrlKey);
10781                 this.lastSelection = item;
10782             }
10783             e.preventDefault();
10784         }
10785         return true;
10786     },
10787
10788     /**
10789      * Get the number of selected nodes.
10790      * @return {Number}
10791      */
10792     getSelectionCount : function(){
10793         return this.selections.length;
10794     },
10795
10796     /**
10797      * Get the currently selected nodes.
10798      * @return {Array} An array of HTMLElements
10799      */
10800     getSelectedNodes : function(){
10801         return this.selections;
10802     },
10803
10804     /**
10805      * Get the indexes of the selected nodes.
10806      * @return {Array}
10807      */
10808     getSelectedIndexes : function(){
10809         var indexes = [], s = this.selections;
10810         for(var i = 0, len = s.length; i < len; i++){
10811             indexes.push(s[i].nodeIndex);
10812         }
10813         return indexes;
10814     },
10815
10816     /**
10817      * Clear all selections
10818      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10819      */
10820     clearSelections : function(suppressEvent){
10821         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10822             this.cmp.elements = this.selections;
10823             this.cmp.removeClass(this.selectedClass);
10824             this.selections = [];
10825             if(!suppressEvent){
10826                 this.fireEvent("selectionchange", this, this.selections);
10827             }
10828         }
10829     },
10830
10831     /**
10832      * Returns true if the passed node is selected
10833      * @param {HTMLElement/Number} node The node or node index
10834      * @return {Boolean}
10835      */
10836     isSelected : function(node){
10837         var s = this.selections;
10838         if(s.length < 1){
10839             return false;
10840         }
10841         node = this.getNode(node);
10842         return s.indexOf(node) !== -1;
10843     },
10844
10845     /**
10846      * Selects nodes.
10847      * @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
10848      * @param {Boolean} keepExisting (optional) true to keep existing selections
10849      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10850      */
10851     select : function(nodeInfo, keepExisting, suppressEvent){
10852         if(nodeInfo instanceof Array){
10853             if(!keepExisting){
10854                 this.clearSelections(true);
10855             }
10856             for(var i = 0, len = nodeInfo.length; i < len; i++){
10857                 this.select(nodeInfo[i], true, true);
10858             }
10859             return;
10860         } 
10861         var node = this.getNode(nodeInfo);
10862         if(!node || this.isSelected(node)){
10863             return; // already selected.
10864         }
10865         if(!keepExisting){
10866             this.clearSelections(true);
10867         }
10868         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10869             Roo.fly(node).addClass(this.selectedClass);
10870             this.selections.push(node);
10871             if(!suppressEvent){
10872                 this.fireEvent("selectionchange", this, this.selections);
10873             }
10874         }
10875         
10876         
10877     },
10878       /**
10879      * Unselects nodes.
10880      * @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
10881      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10882      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10883      */
10884     unselect : function(nodeInfo, keepExisting, suppressEvent)
10885     {
10886         if(nodeInfo instanceof Array){
10887             Roo.each(this.selections, function(s) {
10888                 this.unselect(s, nodeInfo);
10889             }, this);
10890             return;
10891         }
10892         var node = this.getNode(nodeInfo);
10893         if(!node || !this.isSelected(node)){
10894             Roo.log("not selected");
10895             return; // not selected.
10896         }
10897         // fireevent???
10898         var ns = [];
10899         Roo.each(this.selections, function(s) {
10900             if (s == node ) {
10901                 Roo.fly(node).removeClass(this.selectedClass);
10902
10903                 return;
10904             }
10905             ns.push(s);
10906         },this);
10907         
10908         this.selections= ns;
10909         this.fireEvent("selectionchange", this, this.selections);
10910     },
10911
10912     /**
10913      * Gets a template node.
10914      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10915      * @return {HTMLElement} The node or null if it wasn't found
10916      */
10917     getNode : function(nodeInfo){
10918         if(typeof nodeInfo == "string"){
10919             return document.getElementById(nodeInfo);
10920         }else if(typeof nodeInfo == "number"){
10921             return this.nodes[nodeInfo];
10922         }
10923         return nodeInfo;
10924     },
10925
10926     /**
10927      * Gets a range template nodes.
10928      * @param {Number} startIndex
10929      * @param {Number} endIndex
10930      * @return {Array} An array of nodes
10931      */
10932     getNodes : function(start, end){
10933         var ns = this.nodes;
10934         start = start || 0;
10935         end = typeof end == "undefined" ? ns.length - 1 : end;
10936         var nodes = [];
10937         if(start <= end){
10938             for(var i = start; i <= end; i++){
10939                 nodes.push(ns[i]);
10940             }
10941         } else{
10942             for(var i = start; i >= end; i--){
10943                 nodes.push(ns[i]);
10944             }
10945         }
10946         return nodes;
10947     },
10948
10949     /**
10950      * Finds the index of the passed node
10951      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10952      * @return {Number} The index of the node or -1
10953      */
10954     indexOf : function(node){
10955         node = this.getNode(node);
10956         if(typeof node.nodeIndex == "number"){
10957             return node.nodeIndex;
10958         }
10959         var ns = this.nodes;
10960         for(var i = 0, len = ns.length; i < len; i++){
10961             if(ns[i] == node){
10962                 return i;
10963             }
10964         }
10965         return -1;
10966     }
10967 });
10968 /*
10969  * - LGPL
10970  *
10971  * based on jquery fullcalendar
10972  * 
10973  */
10974
10975 Roo.bootstrap = Roo.bootstrap || {};
10976 /**
10977  * @class Roo.bootstrap.Calendar
10978  * @extends Roo.bootstrap.Component
10979  * Bootstrap Calendar class
10980  * @cfg {Boolean} loadMask (true|false) default false
10981  * @cfg {Object} header generate the user specific header of the calendar, default false
10982
10983  * @constructor
10984  * Create a new Container
10985  * @param {Object} config The config object
10986  */
10987
10988
10989
10990 Roo.bootstrap.Calendar = function(config){
10991     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10992      this.addEvents({
10993         /**
10994              * @event select
10995              * Fires when a date is selected
10996              * @param {DatePicker} this
10997              * @param {Date} date The selected date
10998              */
10999         'select': true,
11000         /**
11001              * @event monthchange
11002              * Fires when the displayed month changes 
11003              * @param {DatePicker} this
11004              * @param {Date} date The selected month
11005              */
11006         'monthchange': true,
11007         /**
11008              * @event evententer
11009              * Fires when mouse over an event
11010              * @param {Calendar} this
11011              * @param {event} Event
11012              */
11013         'evententer': true,
11014         /**
11015              * @event eventleave
11016              * Fires when the mouse leaves an
11017              * @param {Calendar} this
11018              * @param {event}
11019              */
11020         'eventleave': true,
11021         /**
11022              * @event eventclick
11023              * Fires when the mouse click an
11024              * @param {Calendar} this
11025              * @param {event}
11026              */
11027         'eventclick': true
11028         
11029     });
11030
11031 };
11032
11033 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11034     
11035      /**
11036      * @cfg {Number} startDay
11037      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11038      */
11039     startDay : 0,
11040     
11041     loadMask : false,
11042     
11043     header : false,
11044       
11045     getAutoCreate : function(){
11046         
11047         
11048         var fc_button = function(name, corner, style, content ) {
11049             return Roo.apply({},{
11050                 tag : 'span',
11051                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11052                          (corner.length ?
11053                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11054                             ''
11055                         ),
11056                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11057                 unselectable: 'on'
11058             });
11059         };
11060         
11061         var header = {};
11062         
11063         if(!this.header){
11064             header = {
11065                 tag : 'table',
11066                 cls : 'fc-header',
11067                 style : 'width:100%',
11068                 cn : [
11069                     {
11070                         tag: 'tr',
11071                         cn : [
11072                             {
11073                                 tag : 'td',
11074                                 cls : 'fc-header-left',
11075                                 cn : [
11076                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11077                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11078                                     { tag: 'span', cls: 'fc-header-space' },
11079                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11080
11081
11082                                 ]
11083                             },
11084
11085                             {
11086                                 tag : 'td',
11087                                 cls : 'fc-header-center',
11088                                 cn : [
11089                                     {
11090                                         tag: 'span',
11091                                         cls: 'fc-header-title',
11092                                         cn : {
11093                                             tag: 'H2',
11094                                             html : 'month / year'
11095                                         }
11096                                     }
11097
11098                                 ]
11099                             },
11100                             {
11101                                 tag : 'td',
11102                                 cls : 'fc-header-right',
11103                                 cn : [
11104                               /*      fc_button('month', 'left', '', 'month' ),
11105                                     fc_button('week', '', '', 'week' ),
11106                                     fc_button('day', 'right', '', 'day' )
11107                                 */    
11108
11109                                 ]
11110                             }
11111
11112                         ]
11113                     }
11114                 ]
11115             };
11116         }
11117         
11118         header = this.header;
11119         
11120        
11121         var cal_heads = function() {
11122             var ret = [];
11123             // fixme - handle this.
11124             
11125             for (var i =0; i < Date.dayNames.length; i++) {
11126                 var d = Date.dayNames[i];
11127                 ret.push({
11128                     tag: 'th',
11129                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11130                     html : d.substring(0,3)
11131                 });
11132                 
11133             }
11134             ret[0].cls += ' fc-first';
11135             ret[6].cls += ' fc-last';
11136             return ret;
11137         };
11138         var cal_cell = function(n) {
11139             return  {
11140                 tag: 'td',
11141                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11142                 cn : [
11143                     {
11144                         cn : [
11145                             {
11146                                 cls: 'fc-day-number',
11147                                 html: 'D'
11148                             },
11149                             {
11150                                 cls: 'fc-day-content',
11151                              
11152                                 cn : [
11153                                      {
11154                                         style: 'position: relative;' // height: 17px;
11155                                     }
11156                                 ]
11157                             }
11158                             
11159                             
11160                         ]
11161                     }
11162                 ]
11163                 
11164             }
11165         };
11166         var cal_rows = function() {
11167             
11168             var ret = []
11169             for (var r = 0; r < 6; r++) {
11170                 var row= {
11171                     tag : 'tr',
11172                     cls : 'fc-week',
11173                     cn : []
11174                 };
11175                 
11176                 for (var i =0; i < Date.dayNames.length; i++) {
11177                     var d = Date.dayNames[i];
11178                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11179
11180                 }
11181                 row.cn[0].cls+=' fc-first';
11182                 row.cn[0].cn[0].style = 'min-height:90px';
11183                 row.cn[6].cls+=' fc-last';
11184                 ret.push(row);
11185                 
11186             }
11187             ret[0].cls += ' fc-first';
11188             ret[4].cls += ' fc-prev-last';
11189             ret[5].cls += ' fc-last';
11190             return ret;
11191             
11192         };
11193         
11194         var cal_table = {
11195             tag: 'table',
11196             cls: 'fc-border-separate',
11197             style : 'width:100%',
11198             cellspacing  : 0,
11199             cn : [
11200                 { 
11201                     tag: 'thead',
11202                     cn : [
11203                         { 
11204                             tag: 'tr',
11205                             cls : 'fc-first fc-last',
11206                             cn : cal_heads()
11207                         }
11208                     ]
11209                 },
11210                 { 
11211                     tag: 'tbody',
11212                     cn : cal_rows()
11213                 }
11214                   
11215             ]
11216         };
11217          
11218          var cfg = {
11219             cls : 'fc fc-ltr',
11220             cn : [
11221                 header,
11222                 {
11223                     cls : 'fc-content',
11224                     style : "position: relative;",
11225                     cn : [
11226                         {
11227                             cls : 'fc-view fc-view-month fc-grid',
11228                             style : 'position: relative',
11229                             unselectable : 'on',
11230                             cn : [
11231                                 {
11232                                     cls : 'fc-event-container',
11233                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11234                                 },
11235                                 cal_table
11236                             ]
11237                         }
11238                     ]
11239     
11240                 }
11241            ] 
11242             
11243         };
11244         
11245          
11246         
11247         return cfg;
11248     },
11249     
11250     
11251     initEvents : function()
11252     {
11253         if(!this.store){
11254             throw "can not find store for calendar";
11255         }
11256         
11257         var mark = {
11258             tag: "div",
11259             cls:"x-dlg-mask",
11260             style: "text-align:center",
11261             cn: [
11262                 {
11263                     tag: "div",
11264                     style: "background-color:white;width:50%;margin:250 auto",
11265                     cn: [
11266                         {
11267                             tag: "img",
11268                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11269                         },
11270                         {
11271                             tag: "span",
11272                             html: "Loading"
11273                         }
11274                         
11275                     ]
11276                 }
11277             ]
11278         }
11279         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11280         
11281         var size = this.el.select('.fc-content', true).first().getSize();
11282         this.maskEl.setSize(size.width, size.height);
11283         this.maskEl.enableDisplayMode("block");
11284         if(!this.loadMask){
11285             this.maskEl.hide();
11286         }
11287         
11288         this.store = Roo.factory(this.store, Roo.data);
11289         this.store.on('load', this.onLoad, this);
11290         this.store.on('beforeload', this.onBeforeLoad, this);
11291         
11292         this.resize();
11293         
11294         this.cells = this.el.select('.fc-day',true);
11295         //Roo.log(this.cells);
11296         this.textNodes = this.el.query('.fc-day-number');
11297         this.cells.addClassOnOver('fc-state-hover');
11298         
11299         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11300         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11301         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11302         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11303         
11304         this.on('monthchange', this.onMonthChange, this);
11305         
11306         this.update(new Date().clearTime());
11307     },
11308     
11309     resize : function() {
11310         var sz  = this.el.getSize();
11311         
11312         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11313         this.el.select('.fc-day-content div',true).setHeight(34);
11314     },
11315     
11316     
11317     // private
11318     showPrevMonth : function(e){
11319         this.update(this.activeDate.add("mo", -1));
11320     },
11321     showToday : function(e){
11322         this.update(new Date().clearTime());
11323     },
11324     // private
11325     showNextMonth : function(e){
11326         this.update(this.activeDate.add("mo", 1));
11327     },
11328
11329     // private
11330     showPrevYear : function(){
11331         this.update(this.activeDate.add("y", -1));
11332     },
11333
11334     // private
11335     showNextYear : function(){
11336         this.update(this.activeDate.add("y", 1));
11337     },
11338
11339     
11340    // private
11341     update : function(date)
11342     {
11343         var vd = this.activeDate;
11344         this.activeDate = date;
11345 //        if(vd && this.el){
11346 //            var t = date.getTime();
11347 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11348 //                Roo.log('using add remove');
11349 //                
11350 //                this.fireEvent('monthchange', this, date);
11351 //                
11352 //                this.cells.removeClass("fc-state-highlight");
11353 //                this.cells.each(function(c){
11354 //                   if(c.dateValue == t){
11355 //                       c.addClass("fc-state-highlight");
11356 //                       setTimeout(function(){
11357 //                            try{c.dom.firstChild.focus();}catch(e){}
11358 //                       }, 50);
11359 //                       return false;
11360 //                   }
11361 //                   return true;
11362 //                });
11363 //                return;
11364 //            }
11365 //        }
11366         
11367         var days = date.getDaysInMonth();
11368         
11369         var firstOfMonth = date.getFirstDateOfMonth();
11370         var startingPos = firstOfMonth.getDay()-this.startDay;
11371         
11372         if(startingPos < this.startDay){
11373             startingPos += 7;
11374         }
11375         
11376         var pm = date.add(Date.MONTH, -1);
11377         var prevStart = pm.getDaysInMonth()-startingPos;
11378 //        
11379         this.cells = this.el.select('.fc-day',true);
11380         this.textNodes = this.el.query('.fc-day-number');
11381         this.cells.addClassOnOver('fc-state-hover');
11382         
11383         var cells = this.cells.elements;
11384         var textEls = this.textNodes;
11385         
11386         Roo.each(cells, function(cell){
11387             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11388         });
11389         
11390         days += startingPos;
11391
11392         // convert everything to numbers so it's fast
11393         var day = 86400000;
11394         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11395         //Roo.log(d);
11396         //Roo.log(pm);
11397         //Roo.log(prevStart);
11398         
11399         var today = new Date().clearTime().getTime();
11400         var sel = date.clearTime().getTime();
11401         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11402         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11403         var ddMatch = this.disabledDatesRE;
11404         var ddText = this.disabledDatesText;
11405         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11406         var ddaysText = this.disabledDaysText;
11407         var format = this.format;
11408         
11409         var setCellClass = function(cal, cell){
11410             
11411             //Roo.log('set Cell Class');
11412             cell.title = "";
11413             var t = d.getTime();
11414             
11415             //Roo.log(d);
11416             
11417             cell.dateValue = t;
11418             if(t == today){
11419                 cell.className += " fc-today";
11420                 cell.className += " fc-state-highlight";
11421                 cell.title = cal.todayText;
11422             }
11423             if(t == sel){
11424                 // disable highlight in other month..
11425                 //cell.className += " fc-state-highlight";
11426                 
11427             }
11428             // disabling
11429             if(t < min) {
11430                 cell.className = " fc-state-disabled";
11431                 cell.title = cal.minText;
11432                 return;
11433             }
11434             if(t > max) {
11435                 cell.className = " fc-state-disabled";
11436                 cell.title = cal.maxText;
11437                 return;
11438             }
11439             if(ddays){
11440                 if(ddays.indexOf(d.getDay()) != -1){
11441                     cell.title = ddaysText;
11442                     cell.className = " fc-state-disabled";
11443                 }
11444             }
11445             if(ddMatch && format){
11446                 var fvalue = d.dateFormat(format);
11447                 if(ddMatch.test(fvalue)){
11448                     cell.title = ddText.replace("%0", fvalue);
11449                     cell.className = " fc-state-disabled";
11450                 }
11451             }
11452             
11453             if (!cell.initialClassName) {
11454                 cell.initialClassName = cell.dom.className;
11455             }
11456             
11457             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11458         };
11459
11460         var i = 0;
11461         
11462         for(; i < startingPos; i++) {
11463             textEls[i].innerHTML = (++prevStart);
11464             d.setDate(d.getDate()+1);
11465             
11466             cells[i].className = "fc-past fc-other-month";
11467             setCellClass(this, cells[i]);
11468         }
11469         
11470         var intDay = 0;
11471         
11472         for(; i < days; i++){
11473             intDay = i - startingPos + 1;
11474             textEls[i].innerHTML = (intDay);
11475             d.setDate(d.getDate()+1);
11476             
11477             cells[i].className = ''; // "x-date-active";
11478             setCellClass(this, cells[i]);
11479         }
11480         var extraDays = 0;
11481         
11482         for(; i < 42; i++) {
11483             textEls[i].innerHTML = (++extraDays);
11484             d.setDate(d.getDate()+1);
11485             
11486             cells[i].className = "fc-future fc-other-month";
11487             setCellClass(this, cells[i]);
11488         }
11489         
11490         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11491         
11492         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11493         
11494         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11495         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11496         
11497         if(totalRows != 6){
11498             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11499             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11500         }
11501         
11502         this.fireEvent('monthchange', this, date);
11503         
11504         
11505         /*
11506         if(!this.internalRender){
11507             var main = this.el.dom.firstChild;
11508             var w = main.offsetWidth;
11509             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11510             Roo.fly(main).setWidth(w);
11511             this.internalRender = true;
11512             // opera does not respect the auto grow header center column
11513             // then, after it gets a width opera refuses to recalculate
11514             // without a second pass
11515             if(Roo.isOpera && !this.secondPass){
11516                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11517                 this.secondPass = true;
11518                 this.update.defer(10, this, [date]);
11519             }
11520         }
11521         */
11522         
11523     },
11524     
11525     findCell : function(dt) {
11526         dt = dt.clearTime().getTime();
11527         var ret = false;
11528         this.cells.each(function(c){
11529             //Roo.log("check " +c.dateValue + '?=' + dt);
11530             if(c.dateValue == dt){
11531                 ret = c;
11532                 return false;
11533             }
11534             return true;
11535         });
11536         
11537         return ret;
11538     },
11539     
11540     findCells : function(ev) {
11541         var s = ev.start.clone().clearTime().getTime();
11542        // Roo.log(s);
11543         var e= ev.end.clone().clearTime().getTime();
11544        // Roo.log(e);
11545         var ret = [];
11546         this.cells.each(function(c){
11547              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11548             
11549             if(c.dateValue > e){
11550                 return ;
11551             }
11552             if(c.dateValue < s){
11553                 return ;
11554             }
11555             ret.push(c);
11556         });
11557         
11558         return ret;    
11559     },
11560     
11561     findBestRow: function(cells)
11562     {
11563         var ret = 0;
11564         
11565         for (var i =0 ; i < cells.length;i++) {
11566             ret  = Math.max(cells[i].rows || 0,ret);
11567         }
11568         return ret;
11569         
11570     },
11571     
11572     
11573     addItem : function(ev)
11574     {
11575         // look for vertical location slot in
11576         var cells = this.findCells(ev);
11577         
11578         ev.row = this.findBestRow(cells);
11579         
11580         // work out the location.
11581         
11582         var crow = false;
11583         var rows = [];
11584         for(var i =0; i < cells.length; i++) {
11585             if (!crow) {
11586                 crow = {
11587                     start : cells[i],
11588                     end :  cells[i]
11589                 };
11590                 continue;
11591             }
11592             if (crow.start.getY() == cells[i].getY()) {
11593                 // on same row.
11594                 crow.end = cells[i];
11595                 continue;
11596             }
11597             // different row.
11598             rows.push(crow);
11599             crow = {
11600                 start: cells[i],
11601                 end : cells[i]
11602             };
11603             
11604         }
11605         
11606         rows.push(crow);
11607         ev.els = [];
11608         ev.rows = rows;
11609         ev.cells = cells;
11610         for (var i = 0; i < cells.length;i++) {
11611             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11612             
11613         }
11614         
11615         this.calevents.push(ev);
11616     },
11617     
11618     clearEvents: function() {
11619         
11620         if(!this.calevents){
11621             return;
11622         }
11623         
11624         Roo.each(this.cells.elements, function(c){
11625             c.rows = 0;
11626         });
11627         
11628         Roo.each(this.calevents, function(e) {
11629             Roo.each(e.els, function(el) {
11630                 el.un('mouseenter' ,this.onEventEnter, this);
11631                 el.un('mouseleave' ,this.onEventLeave, this);
11632                 el.remove();
11633             },this);
11634         },this);
11635         
11636     },
11637     
11638     renderEvents: function()
11639     {   
11640         // first make sure there is enough space..
11641         
11642         this.cells.each(function(c) {
11643         
11644             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11645         });
11646         
11647         for (var e = 0; e < this.calevents.length; e++) {
11648             var ev = this.calevents[e];
11649             var cells = ev.cells;
11650             var rows = ev.rows;
11651             
11652             for(var i =0; i < rows.length; i++) {
11653                 
11654                  
11655                 // how many rows should it span..
11656                 
11657                 var  cfg = {
11658                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11659                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11660                     
11661                     unselectable : "on",
11662                     cn : [
11663                         {
11664                             cls: 'fc-event-inner',
11665                             cn : [
11666 //                                {
11667 //                                  tag:'span',
11668 //                                  cls: 'fc-event-time',
11669 //                                  html : cells.length > 1 ? '' : ev.time
11670 //                                },
11671                                 {
11672                                   tag:'span',
11673                                   cls: 'fc-event-title',
11674                                   html : String.format('{0}', ev.title)
11675                                 }
11676                                 
11677                                 
11678                             ]
11679                         },
11680                         {
11681                             cls: 'ui-resizable-handle ui-resizable-e',
11682                             html : '&nbsp;&nbsp;&nbsp'
11683                         }
11684                         
11685                     ]
11686                 };
11687                 if (i == 0) {
11688                     cfg.cls += ' fc-event-start';
11689                 }
11690                 if ((i+1) == rows.length) {
11691                     cfg.cls += ' fc-event-end';
11692                 }
11693                 
11694                 var ctr = this.el.select('.fc-event-container',true).first();
11695                 var cg = ctr.createChild(cfg);
11696                 
11697                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11698                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11699                 cg.on('click', this.onEventClick, this, ev);
11700                 
11701                 ev.els.push(cg);
11702                 
11703                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11704                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11705                 //Roo.log(cg);
11706                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11707                 cg.setWidth(ebox.right - sbox.x -2);
11708             }
11709             
11710             
11711         }
11712         
11713     },
11714     
11715     onEventEnter: function (e, el,event,d) {
11716         this.fireEvent('evententer', this, el, event);
11717     },
11718     
11719     onEventLeave: function (e, el,event,d) {
11720         this.fireEvent('eventleave', this, el, event);
11721     },
11722     
11723     onEventClick: function (e, el,event,d) {
11724         this.fireEvent('eventclick', this, el, event);
11725     },
11726     
11727     onMonthChange: function () {
11728         this.store.load();
11729     },
11730     
11731     onLoad: function () 
11732     {   
11733         this.calevents = [];
11734         var cal = this;
11735         
11736         if(this.store.getCount() > 0){
11737             this.store.data.each(function(d){
11738                cal.addItem({
11739                     id : d.data.id,
11740                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11741                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11742                     time : d.data.start_time,
11743                     title : d.data.title,
11744                     description : d.data.description,
11745                     venue : d.data.venue
11746                 });
11747             });
11748         }
11749         
11750         this.renderEvents();
11751         
11752         if(this.loadMask){
11753             this.maskEl.hide();
11754         }
11755     },
11756     
11757     onBeforeLoad: function()
11758     {
11759         this.clearEvents();
11760         
11761         if(this.loadMask){
11762             this.maskEl.show();
11763         }
11764     }
11765 });
11766
11767  
11768  /*
11769  * - LGPL
11770  *
11771  * element
11772  * 
11773  */
11774
11775 /**
11776  * @class Roo.bootstrap.Popover
11777  * @extends Roo.bootstrap.Component
11778  * Bootstrap Popover class
11779  * @cfg {String} html contents of the popover   (or false to use children..)
11780  * @cfg {String} title of popover (or false to hide)
11781  * @cfg {String} placement how it is placed
11782  * @cfg {String} trigger click || hover (or false to trigger manually)
11783  * @cfg {String} over what (parent or false to trigger manually.)
11784  * 
11785  * @constructor
11786  * Create a new Popover
11787  * @param {Object} config The config object
11788  */
11789
11790 Roo.bootstrap.Popover = function(config){
11791     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11792 };
11793
11794 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11795     
11796     title: 'Fill in a title',
11797     html: false,
11798     
11799     placement : 'right',
11800     trigger : 'hover', // hover
11801     
11802     over: 'parent',
11803     
11804     can_build_overlaid : false,
11805     
11806     getChildContainer : function()
11807     {
11808         return this.el.select('.popover-content',true).first();
11809     },
11810     
11811     getAutoCreate : function(){
11812          Roo.log('make popover?');
11813         var cfg = {
11814            cls : 'popover roo-dynamic',
11815            style: 'display:block',
11816            cn : [
11817                 {
11818                     cls : 'arrow'
11819                 },
11820                 {
11821                     cls : 'popover-inner',
11822                     cn : [
11823                         {
11824                             tag: 'h3',
11825                             cls: 'popover-title',
11826                             html : this.title
11827                         },
11828                         {
11829                             cls : 'popover-content',
11830                             html : this.html
11831                         }
11832                     ]
11833                     
11834                 }
11835            ]
11836         };
11837         
11838         return cfg;
11839     },
11840     setTitle: function(str)
11841     {
11842         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11843     },
11844     setContent: function(str)
11845     {
11846         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11847     },
11848     // as it get's added to the bottom of the page.
11849     onRender : function(ct, position)
11850     {
11851         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11852         if(!this.el){
11853             var cfg = Roo.apply({},  this.getAutoCreate());
11854             cfg.id = Roo.id();
11855             
11856             if (this.cls) {
11857                 cfg.cls += ' ' + this.cls;
11858             }
11859             if (this.style) {
11860                 cfg.style = this.style;
11861             }
11862             Roo.log("adding to ")
11863             this.el = Roo.get(document.body).createChild(cfg, position);
11864             Roo.log(this.el);
11865         }
11866         this.initEvents();
11867     },
11868     
11869     initEvents : function()
11870     {
11871         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11872         this.el.enableDisplayMode('block');
11873         this.el.hide();
11874         if (this.over === false) {
11875             return; 
11876         }
11877         if (this.triggers === false) {
11878             return;
11879         }
11880         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11881         var triggers = this.trigger ? this.trigger.split(' ') : [];
11882         Roo.each(triggers, function(trigger) {
11883         
11884             if (trigger == 'click') {
11885                 on_el.on('click', this.toggle, this);
11886             } else if (trigger != 'manual') {
11887                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11888                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11889       
11890                 on_el.on(eventIn  ,this.enter, this);
11891                 on_el.on(eventOut, this.leave, this);
11892             }
11893         }, this);
11894         
11895     },
11896     
11897     
11898     // private
11899     timeout : null,
11900     hoverState : null,
11901     
11902     toggle : function () {
11903         this.hoverState == 'in' ? this.leave() : this.enter();
11904     },
11905     
11906     enter : function () {
11907        
11908     
11909         clearTimeout(this.timeout);
11910     
11911         this.hoverState = 'in'
11912     
11913         if (!this.delay || !this.delay.show) {
11914             this.show();
11915             return 
11916         }
11917         var _t = this;
11918         this.timeout = setTimeout(function () {
11919             if (_t.hoverState == 'in') {
11920                 _t.show();
11921             }
11922         }, this.delay.show)
11923     },
11924     leave : function() {
11925         clearTimeout(this.timeout);
11926     
11927         this.hoverState = 'out'
11928     
11929         if (!this.delay || !this.delay.hide) {
11930             this.hide();
11931             return 
11932         }
11933         var _t = this;
11934         this.timeout = setTimeout(function () {
11935             if (_t.hoverState == 'out') {
11936                 _t.hide();
11937             }
11938         }, this.delay.hide)
11939     },
11940     
11941     show : function (on_el)
11942     {
11943         if (!on_el) {
11944             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11945         }
11946         // set content.
11947         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11948         if (this.html !== false) {
11949             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11950         }
11951         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11952         if (!this.title.length) {
11953             this.el.select('.popover-title',true).hide();
11954         }
11955         
11956         var placement = typeof this.placement == 'function' ?
11957             this.placement.call(this, this.el, on_el) :
11958             this.placement;
11959             
11960         var autoToken = /\s?auto?\s?/i;
11961         var autoPlace = autoToken.test(placement);
11962         if (autoPlace) {
11963             placement = placement.replace(autoToken, '') || 'top';
11964         }
11965         
11966         //this.el.detach()
11967         //this.el.setXY([0,0]);
11968         this.el.show();
11969         this.el.dom.style.display='block';
11970         this.el.addClass(placement);
11971         
11972         //this.el.appendTo(on_el);
11973         
11974         var p = this.getPosition();
11975         var box = this.el.getBox();
11976         
11977         if (autoPlace) {
11978             // fixme..
11979         }
11980         var align = Roo.bootstrap.Popover.alignment[placement]
11981         this.el.alignTo(on_el, align[0],align[1]);
11982         //var arrow = this.el.select('.arrow',true).first();
11983         //arrow.set(align[2], 
11984         
11985         this.el.addClass('in');
11986         this.hoverState = null;
11987         
11988         if (this.el.hasClass('fade')) {
11989             // fade it?
11990         }
11991         
11992     },
11993     hide : function()
11994     {
11995         this.el.setXY([0,0]);
11996         this.el.removeClass('in');
11997         this.el.hide();
11998         
11999     }
12000     
12001 });
12002
12003 Roo.bootstrap.Popover.alignment = {
12004     'left' : ['r-l', [-10,0], 'right'],
12005     'right' : ['l-r', [10,0], 'left'],
12006     'bottom' : ['t-b', [0,10], 'top'],
12007     'top' : [ 'b-t', [0,-10], 'bottom']
12008 };
12009
12010  /*
12011  * - LGPL
12012  *
12013  * Progress
12014  * 
12015  */
12016
12017 /**
12018  * @class Roo.bootstrap.Progress
12019  * @extends Roo.bootstrap.Component
12020  * Bootstrap Progress class
12021  * @cfg {Boolean} striped striped of the progress bar
12022  * @cfg {Boolean} active animated of the progress bar
12023  * 
12024  * 
12025  * @constructor
12026  * Create a new Progress
12027  * @param {Object} config The config object
12028  */
12029
12030 Roo.bootstrap.Progress = function(config){
12031     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12032 };
12033
12034 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12035     
12036     striped : false,
12037     active: false,
12038     
12039     getAutoCreate : function(){
12040         var cfg = {
12041             tag: 'div',
12042             cls: 'progress'
12043         };
12044         
12045         
12046         if(this.striped){
12047             cfg.cls += ' progress-striped';
12048         }
12049       
12050         if(this.active){
12051             cfg.cls += ' active';
12052         }
12053         
12054         
12055         return cfg;
12056     }
12057    
12058 });
12059
12060  
12061
12062  /*
12063  * - LGPL
12064  *
12065  * ProgressBar
12066  * 
12067  */
12068
12069 /**
12070  * @class Roo.bootstrap.ProgressBar
12071  * @extends Roo.bootstrap.Component
12072  * Bootstrap ProgressBar class
12073  * @cfg {Number} aria_valuenow aria-value now
12074  * @cfg {Number} aria_valuemin aria-value min
12075  * @cfg {Number} aria_valuemax aria-value max
12076  * @cfg {String} label label for the progress bar
12077  * @cfg {String} panel (success | info | warning | danger )
12078  * @cfg {String} role role of the progress bar
12079  * @cfg {String} sr_only text
12080  * 
12081  * 
12082  * @constructor
12083  * Create a new ProgressBar
12084  * @param {Object} config The config object
12085  */
12086
12087 Roo.bootstrap.ProgressBar = function(config){
12088     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12089 };
12090
12091 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12092     
12093     aria_valuenow : 0,
12094     aria_valuemin : 0,
12095     aria_valuemax : 100,
12096     label : false,
12097     panel : false,
12098     role : false,
12099     sr_only: false,
12100     
12101     getAutoCreate : function()
12102     {
12103         
12104         var cfg = {
12105             tag: 'div',
12106             cls: 'progress-bar',
12107             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12108         };
12109         
12110         if(this.sr_only){
12111             cfg.cn = {
12112                 tag: 'span',
12113                 cls: 'sr-only',
12114                 html: this.sr_only
12115             }
12116         }
12117         
12118         if(this.role){
12119             cfg.role = this.role;
12120         }
12121         
12122         if(this.aria_valuenow){
12123             cfg['aria-valuenow'] = this.aria_valuenow;
12124         }
12125         
12126         if(this.aria_valuemin){
12127             cfg['aria-valuemin'] = this.aria_valuemin;
12128         }
12129         
12130         if(this.aria_valuemax){
12131             cfg['aria-valuemax'] = this.aria_valuemax;
12132         }
12133         
12134         if(this.label && !this.sr_only){
12135             cfg.html = this.label;
12136         }
12137         
12138         if(this.panel){
12139             cfg.cls += ' progress-bar-' + this.panel;
12140         }
12141         
12142         return cfg;
12143     },
12144     
12145     update : function(aria_valuenow)
12146     {
12147         this.aria_valuenow = aria_valuenow;
12148         
12149         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12150     }
12151    
12152 });
12153
12154  
12155
12156  /*
12157  * - LGPL
12158  *
12159  * TabPanel
12160  * 
12161  */
12162
12163 /**
12164  * @class Roo.bootstrap.TabPanel
12165  * @extends Roo.bootstrap.Component
12166  * Bootstrap TabPanel class
12167  * @cfg {Boolean} active panel active
12168  * @cfg {String} html panel content
12169  * @cfg {String} tabId tab relate id
12170  * @cfg {String} navId The navbar which triggers show hide
12171  * 
12172  * 
12173  * @constructor
12174  * Create a new TabPanel
12175  * @param {Object} config The config object
12176  */
12177
12178 Roo.bootstrap.TabPanel = function(config){
12179     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12180      this.addEvents({
12181         /**
12182              * @event changed
12183              * Fires when the active status changes
12184              * @param {Roo.bootstrap.TabPanel} this
12185              * @param {Boolean} state the new state
12186             
12187          */
12188         'changed': true
12189      });
12190 };
12191
12192 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12193     
12194     active: false,
12195     html: false,
12196     tabId: false,
12197     navId : false,
12198     
12199     getAutoCreate : function(){
12200         var cfg = {
12201             tag: 'div',
12202             cls: 'tab-pane',
12203             html: this.html || ''
12204         };
12205         
12206         if(this.active){
12207             cfg.cls += ' active';
12208         }
12209         
12210         if(this.tabId){
12211             cfg.tabId = this.tabId;
12212         }
12213         
12214         return cfg;
12215     },
12216     onRender : function(ct, position)
12217     {
12218        // Roo.log("Call onRender: " + this.xtype);
12219         
12220         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12221         
12222         if (this.navId && this.tabId) {
12223             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12224             if (!item) {
12225                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12226             } else {
12227                 item.on('changed', function(e, item, state) {
12228                     Roo.log(e);
12229                     this.setActive(state);
12230                 }, this);
12231             }
12232         }
12233         
12234     },
12235     setActive: function(state)
12236     {
12237         Roo.log("panel - set active " + this.tabId + "=" + state);
12238         
12239         this.active = state;
12240         if (!state) {
12241             this.el.removeClass('active');
12242             
12243         } else  if (!this.el.hasClass('active')) {
12244             this.el.addClass('active');
12245         }
12246         this.fireEvent('changed', this, state);
12247     }
12248     
12249     
12250 });
12251  
12252
12253  
12254
12255  /*
12256  * - LGPL
12257  *
12258  * DateField
12259  * 
12260  */
12261
12262 /**
12263  * @class Roo.bootstrap.DateField
12264  * @extends Roo.bootstrap.Input
12265  * Bootstrap DateField class
12266  * @cfg {Number} weekStart default 0
12267  * @cfg {Number} weekStart default 0
12268  * @cfg {Number} viewMode default empty, (months|years)
12269  * @cfg {Number} minViewMode default empty, (months|years)
12270  * @cfg {Number} startDate default -Infinity
12271  * @cfg {Number} endDate default Infinity
12272  * @cfg {Boolean} todayHighlight default false
12273  * @cfg {Boolean} todayBtn default false
12274  * @cfg {Boolean} calendarWeeks default false
12275  * @cfg {Object} daysOfWeekDisabled default empty
12276  * 
12277  * @cfg {Boolean} keyboardNavigation default true
12278  * @cfg {String} language default en
12279  * 
12280  * @constructor
12281  * Create a new DateField
12282  * @param {Object} config The config object
12283  */
12284
12285 Roo.bootstrap.DateField = function(config){
12286     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12287      this.addEvents({
12288             /**
12289              * @event show
12290              * Fires when this field show.
12291              * @param {Roo.bootstrap.DateField} this
12292              * @param {Mixed} date The date value
12293              */
12294             show : true,
12295             /**
12296              * @event show
12297              * Fires when this field hide.
12298              * @param {Roo.bootstrap.DateField} this
12299              * @param {Mixed} date The date value
12300              */
12301             hide : true,
12302             /**
12303              * @event select
12304              * Fires when select a date.
12305              * @param {Roo.bootstrap.DateField} this
12306              * @param {Mixed} date The date value
12307              */
12308             select : true
12309         });
12310 };
12311
12312 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12313     
12314     /**
12315      * @cfg {String} format
12316      * The default date format string which can be overriden for localization support.  The format must be
12317      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12318      */
12319     format : "m/d/y",
12320     /**
12321      * @cfg {String} altFormats
12322      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12323      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12324      */
12325     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12326     
12327     weekStart : 0,
12328     
12329     viewMode : '',
12330     
12331     minViewMode : '',
12332     
12333     todayHighlight : false,
12334     
12335     todayBtn: false,
12336     
12337     language: 'en',
12338     
12339     keyboardNavigation: true,
12340     
12341     calendarWeeks: false,
12342     
12343     startDate: -Infinity,
12344     
12345     endDate: Infinity,
12346     
12347     daysOfWeekDisabled: [],
12348     
12349     _events: [],
12350     
12351     UTCDate: function()
12352     {
12353         return new Date(Date.UTC.apply(Date, arguments));
12354     },
12355     
12356     UTCToday: function()
12357     {
12358         var today = new Date();
12359         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12360     },
12361     
12362     getDate: function() {
12363             var d = this.getUTCDate();
12364             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12365     },
12366     
12367     getUTCDate: function() {
12368             return this.date;
12369     },
12370     
12371     setDate: function(d) {
12372             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12373     },
12374     
12375     setUTCDate: function(d) {
12376             this.date = d;
12377             this.setValue(this.formatDate(this.date));
12378     },
12379         
12380     onRender: function(ct, position)
12381     {
12382         
12383         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12384         
12385         this.language = this.language || 'en';
12386         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12387         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12388         
12389         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12390         this.format = this.format || 'm/d/y';
12391         this.isInline = false;
12392         this.isInput = true;
12393         this.component = this.el.select('.add-on', true).first() || false;
12394         this.component = (this.component && this.component.length === 0) ? false : this.component;
12395         this.hasInput = this.component && this.inputEL().length;
12396         
12397         if (typeof(this.minViewMode === 'string')) {
12398             switch (this.minViewMode) {
12399                 case 'months':
12400                     this.minViewMode = 1;
12401                     break;
12402                 case 'years':
12403                     this.minViewMode = 2;
12404                     break;
12405                 default:
12406                     this.minViewMode = 0;
12407                     break;
12408             }
12409         }
12410         
12411         if (typeof(this.viewMode === 'string')) {
12412             switch (this.viewMode) {
12413                 case 'months':
12414                     this.viewMode = 1;
12415                     break;
12416                 case 'years':
12417                     this.viewMode = 2;
12418                     break;
12419                 default:
12420                     this.viewMode = 0;
12421                     break;
12422             }
12423         }
12424                 
12425         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12426         
12427         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12428         
12429         this.picker().on('mousedown', this.onMousedown, this);
12430         this.picker().on('click', this.onClick, this);
12431         
12432         this.picker().addClass('datepicker-dropdown');
12433         
12434         this.startViewMode = this.viewMode;
12435         
12436         
12437         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12438             if(!this.calendarWeeks){
12439                 v.remove();
12440                 return;
12441             };
12442             
12443             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12444             v.attr('colspan', function(i, val){
12445                 return parseInt(val) + 1;
12446             });
12447         })
12448                         
12449         
12450         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12451         
12452         this.setStartDate(this.startDate);
12453         this.setEndDate(this.endDate);
12454         
12455         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12456         
12457         this.fillDow();
12458         this.fillMonths();
12459         this.update();
12460         this.showMode();
12461         
12462         if(this.isInline) {
12463             this.show();
12464         }
12465     },
12466     
12467     picker : function()
12468     {
12469         return this.el.select('.datepicker', true).first();
12470     },
12471     
12472     fillDow: function()
12473     {
12474         var dowCnt = this.weekStart;
12475         
12476         var dow = {
12477             tag: 'tr',
12478             cn: [
12479                 
12480             ]
12481         };
12482         
12483         if(this.calendarWeeks){
12484             dow.cn.push({
12485                 tag: 'th',
12486                 cls: 'cw',
12487                 html: '&nbsp;'
12488             })
12489         }
12490         
12491         while (dowCnt < this.weekStart + 7) {
12492             dow.cn.push({
12493                 tag: 'th',
12494                 cls: 'dow',
12495                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12496             });
12497         }
12498         
12499         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12500     },
12501     
12502     fillMonths: function()
12503     {    
12504         var i = 0
12505         var months = this.picker().select('>.datepicker-months td', true).first();
12506         
12507         months.dom.innerHTML = '';
12508         
12509         while (i < 12) {
12510             var month = {
12511                 tag: 'span',
12512                 cls: 'month',
12513                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12514             }
12515             
12516             months.createChild(month);
12517         }
12518         
12519     },
12520     
12521     update: function(){
12522         
12523         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12524         
12525         if (this.date < this.startDate) {
12526             this.viewDate = new Date(this.startDate);
12527         } else if (this.date > this.endDate) {
12528             this.viewDate = new Date(this.endDate);
12529         } else {
12530             this.viewDate = new Date(this.date);
12531         }
12532         
12533         this.fill();
12534     },
12535     
12536     fill: function() {
12537         var d = new Date(this.viewDate),
12538                 year = d.getUTCFullYear(),
12539                 month = d.getUTCMonth(),
12540                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12541                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12542                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12543                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12544                 currentDate = this.date && this.date.valueOf(),
12545                 today = this.UTCToday();
12546         
12547         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12548         
12549 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12550         
12551 //        this.picker.select('>tfoot th.today').
12552 //                                              .text(dates[this.language].today)
12553 //                                              .toggle(this.todayBtn !== false);
12554     
12555         this.updateNavArrows();
12556         this.fillMonths();
12557                                                 
12558         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12559         
12560         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12561          
12562         prevMonth.setUTCDate(day);
12563         
12564         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12565         
12566         var nextMonth = new Date(prevMonth);
12567         
12568         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12569         
12570         nextMonth = nextMonth.valueOf();
12571         
12572         var fillMonths = false;
12573         
12574         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12575         
12576         while(prevMonth.valueOf() < nextMonth) {
12577             var clsName = '';
12578             
12579             if (prevMonth.getUTCDay() === this.weekStart) {
12580                 if(fillMonths){
12581                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12582                 }
12583                     
12584                 fillMonths = {
12585                     tag: 'tr',
12586                     cn: []
12587                 };
12588                 
12589                 if(this.calendarWeeks){
12590                     // ISO 8601: First week contains first thursday.
12591                     // ISO also states week starts on Monday, but we can be more abstract here.
12592                     var
12593                     // Start of current week: based on weekstart/current date
12594                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12595                     // Thursday of this week
12596                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12597                     // First Thursday of year, year from thursday
12598                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12599                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12600                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12601                     
12602                     fillMonths.cn.push({
12603                         tag: 'td',
12604                         cls: 'cw',
12605                         html: calWeek
12606                     });
12607                 }
12608             }
12609             
12610             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12611                 clsName += ' old';
12612             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12613                 clsName += ' new';
12614             }
12615             if (this.todayHighlight &&
12616                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12617                 prevMonth.getUTCMonth() == today.getMonth() &&
12618                 prevMonth.getUTCDate() == today.getDate()) {
12619                 clsName += ' today';
12620             }
12621             
12622             if (currentDate && prevMonth.valueOf() === currentDate) {
12623                 clsName += ' active';
12624             }
12625             
12626             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12627                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12628                     clsName += ' disabled';
12629             }
12630             
12631             fillMonths.cn.push({
12632                 tag: 'td',
12633                 cls: 'day ' + clsName,
12634                 html: prevMonth.getDate()
12635             })
12636             
12637             prevMonth.setDate(prevMonth.getDate()+1);
12638         }
12639           
12640         var currentYear = this.date && this.date.getUTCFullYear();
12641         var currentMonth = this.date && this.date.getUTCMonth();
12642         
12643         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12644         
12645         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12646             v.removeClass('active');
12647             
12648             if(currentYear === year && k === currentMonth){
12649                 v.addClass('active');
12650             }
12651             
12652             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12653                 v.addClass('disabled');
12654             }
12655             
12656         });
12657         
12658         
12659         year = parseInt(year/10, 10) * 10;
12660         
12661         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12662         
12663         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12664         
12665         year -= 1;
12666         for (var i = -1; i < 11; i++) {
12667             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12668                 tag: 'span',
12669                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12670                 html: year
12671             })
12672             
12673             year += 1;
12674         }
12675     },
12676     
12677     showMode: function(dir) {
12678         if (dir) {
12679             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12680         }
12681         Roo.each(this.picker().select('>div',true).elements, function(v){
12682             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12683             v.hide();
12684         });
12685         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12686     },
12687     
12688     place: function()
12689     {
12690         if(this.isInline) return;
12691         
12692         this.picker().removeClass(['bottom', 'top']);
12693         
12694         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12695             /*
12696              * place to the top of element!
12697              *
12698              */
12699             
12700             this.picker().addClass('top');
12701             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12702             
12703             return;
12704         }
12705         
12706         this.picker().addClass('bottom');
12707         
12708         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12709     },
12710     
12711     parseDate : function(value){
12712         if(!value || value instanceof Date){
12713             return value;
12714         }
12715         var v = Date.parseDate(value, this.format);
12716         if (!v && this.useIso) {
12717             v = Date.parseDate(value, 'Y-m-d');
12718         }
12719         if(!v && this.altFormats){
12720             if(!this.altFormatsArray){
12721                 this.altFormatsArray = this.altFormats.split("|");
12722             }
12723             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12724                 v = Date.parseDate(value, this.altFormatsArray[i]);
12725             }
12726         }
12727         return v;
12728     },
12729     
12730     formatDate : function(date, fmt){
12731         return (!date || !(date instanceof Date)) ?
12732         date : date.dateFormat(fmt || this.format);
12733     },
12734     
12735     onFocus : function()
12736     {
12737         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12738         this.show();
12739     },
12740     
12741     onBlur : function()
12742     {
12743         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12744         this.hide();
12745     },
12746     
12747     show : function()
12748     {
12749         this.picker().show();
12750         this.update();
12751         this.place();
12752         
12753         this.fireEvent('show', this, this.date);
12754     },
12755     
12756     hide : function()
12757     {
12758         if(this.isInline) return;
12759         this.picker().hide();
12760         this.viewMode = this.startViewMode;
12761         this.showMode();
12762         
12763         this.fireEvent('hide', this, this.date);
12764         
12765     },
12766     
12767     onMousedown: function(e){
12768         e.stopPropagation();
12769         e.preventDefault();
12770     },
12771     
12772     keyup: function(e){
12773         Roo.bootstrap.DateField.superclass.keyup.call(this);
12774         this.update();
12775         
12776     },
12777
12778     setValue: function(v){
12779         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12780         
12781         this.fireEvent('select', this, this.date);
12782         
12783     },
12784     
12785     fireKey: function(e){
12786         if (!this.picker().isVisible()){
12787             if (e.keyCode == 27) // allow escape to hide and re-show picker
12788                 this.show();
12789             return;
12790         }
12791         var dateChanged = false,
12792         dir, day, month,
12793         newDate, newViewDate;
12794         switch(e.keyCode){
12795             case 27: // escape
12796                 this.hide();
12797                 e.preventDefault();
12798                 break;
12799             case 37: // left
12800             case 39: // right
12801                 if (!this.keyboardNavigation) break;
12802                 dir = e.keyCode == 37 ? -1 : 1;
12803                 
12804                 if (e.ctrlKey){
12805                     newDate = this.moveYear(this.date, dir);
12806                     newViewDate = this.moveYear(this.viewDate, dir);
12807                 } else if (e.shiftKey){
12808                     newDate = this.moveMonth(this.date, dir);
12809                     newViewDate = this.moveMonth(this.viewDate, dir);
12810                 } else {
12811                     newDate = new Date(this.date);
12812                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12813                     newViewDate = new Date(this.viewDate);
12814                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12815                 }
12816                 if (this.dateWithinRange(newDate)){
12817                     this.date = newDate;
12818                     this.viewDate = newViewDate;
12819                     this.setValue(this.formatDate(this.date));
12820                     this.update();
12821                     e.preventDefault();
12822                     dateChanged = true;
12823                 }
12824                 break;
12825             case 38: // up
12826             case 40: // down
12827                 if (!this.keyboardNavigation) break;
12828                 dir = e.keyCode == 38 ? -1 : 1;
12829                 if (e.ctrlKey){
12830                     newDate = this.moveYear(this.date, dir);
12831                     newViewDate = this.moveYear(this.viewDate, dir);
12832                 } else if (e.shiftKey){
12833                     newDate = this.moveMonth(this.date, dir);
12834                     newViewDate = this.moveMonth(this.viewDate, dir);
12835                 } else {
12836                     newDate = new Date(this.date);
12837                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12838                     newViewDate = new Date(this.viewDate);
12839                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12840                 }
12841                 if (this.dateWithinRange(newDate)){
12842                     this.date = newDate;
12843                     this.viewDate = newViewDate;
12844                     this.setValue(this.formatDate(this.date));
12845                     this.update();
12846                     e.preventDefault();
12847                     dateChanged = true;
12848                 }
12849                 break;
12850             case 13: // enter
12851                 this.setValue(this.formatDate(this.date));
12852                 this.hide();
12853                 e.preventDefault();
12854                 break;
12855             case 9: // tab
12856                 this.setValue(this.formatDate(this.date));
12857                 this.hide();
12858                 break;
12859         }
12860     },
12861     
12862     
12863     onClick: function(e) {
12864         e.stopPropagation();
12865         e.preventDefault();
12866         
12867         var target = e.getTarget();
12868         
12869         if(target.nodeName.toLowerCase() === 'i'){
12870             target = Roo.get(target).dom.parentNode;
12871         }
12872         
12873         var nodeName = target.nodeName;
12874         var className = target.className;
12875         var html = target.innerHTML;
12876         
12877         switch(nodeName.toLowerCase()) {
12878             case 'th':
12879                 switch(className) {
12880                     case 'switch':
12881                         this.showMode(1);
12882                         break;
12883                     case 'prev':
12884                     case 'next':
12885                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12886                         switch(this.viewMode){
12887                                 case 0:
12888                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12889                                         break;
12890                                 case 1:
12891                                 case 2:
12892                                         this.viewDate = this.moveYear(this.viewDate, dir);
12893                                         break;
12894                         }
12895                         this.fill();
12896                         break;
12897                     case 'today':
12898                         var date = new Date();
12899                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12900                         this.fill()
12901                         this.setValue(this.formatDate(this.date));
12902                         this.hide();
12903                         break;
12904                 }
12905                 break;
12906             case 'span':
12907                 if (className.indexOf('disabled') === -1) {
12908                     this.viewDate.setUTCDate(1);
12909                     if (className.indexOf('month') !== -1) {
12910                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12911                     } else {
12912                         var year = parseInt(html, 10) || 0;
12913                         this.viewDate.setUTCFullYear(year);
12914                         
12915                     }
12916                     this.showMode(-1);
12917                     this.fill();
12918                 }
12919                 break;
12920                 
12921             case 'td':
12922                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12923                     var day = parseInt(html, 10) || 1;
12924                     var year = this.viewDate.getUTCFullYear(),
12925                         month = this.viewDate.getUTCMonth();
12926
12927                     if (className.indexOf('old') !== -1) {
12928                         if(month === 0 ){
12929                             month = 11;
12930                             year -= 1;
12931                         }else{
12932                             month -= 1;
12933                         }
12934                     } else if (className.indexOf('new') !== -1) {
12935                         if (month == 11) {
12936                             month = 0;
12937                             year += 1;
12938                         } else {
12939                             month += 1;
12940                         }
12941                     }
12942                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12943                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12944                     this.fill();
12945                     this.setValue(this.formatDate(this.date));
12946                     this.hide();
12947                 }
12948                 break;
12949         }
12950     },
12951     
12952     setStartDate: function(startDate){
12953         this.startDate = startDate || -Infinity;
12954         if (this.startDate !== -Infinity) {
12955             this.startDate = this.parseDate(this.startDate);
12956         }
12957         this.update();
12958         this.updateNavArrows();
12959     },
12960
12961     setEndDate: function(endDate){
12962         this.endDate = endDate || Infinity;
12963         if (this.endDate !== Infinity) {
12964             this.endDate = this.parseDate(this.endDate);
12965         }
12966         this.update();
12967         this.updateNavArrows();
12968     },
12969     
12970     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12971         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12972         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12973             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12974         }
12975         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12976             return parseInt(d, 10);
12977         });
12978         this.update();
12979         this.updateNavArrows();
12980     },
12981     
12982     updateNavArrows: function() {
12983         var d = new Date(this.viewDate),
12984         year = d.getUTCFullYear(),
12985         month = d.getUTCMonth();
12986         
12987         Roo.each(this.picker().select('.prev', true).elements, function(v){
12988             v.show();
12989             switch (this.viewMode) {
12990                 case 0:
12991
12992                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12993                         v.hide();
12994                     }
12995                     break;
12996                 case 1:
12997                 case 2:
12998                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12999                         v.hide();
13000                     }
13001                     break;
13002             }
13003         });
13004         
13005         Roo.each(this.picker().select('.next', true).elements, function(v){
13006             v.show();
13007             switch (this.viewMode) {
13008                 case 0:
13009
13010                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13011                         v.hide();
13012                     }
13013                     break;
13014                 case 1:
13015                 case 2:
13016                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13017                         v.hide();
13018                     }
13019                     break;
13020             }
13021         })
13022     },
13023     
13024     moveMonth: function(date, dir){
13025         if (!dir) return date;
13026         var new_date = new Date(date.valueOf()),
13027         day = new_date.getUTCDate(),
13028         month = new_date.getUTCMonth(),
13029         mag = Math.abs(dir),
13030         new_month, test;
13031         dir = dir > 0 ? 1 : -1;
13032         if (mag == 1){
13033             test = dir == -1
13034             // If going back one month, make sure month is not current month
13035             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13036             ? function(){
13037                 return new_date.getUTCMonth() == month;
13038             }
13039             // If going forward one month, make sure month is as expected
13040             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13041             : function(){
13042                 return new_date.getUTCMonth() != new_month;
13043             };
13044             new_month = month + dir;
13045             new_date.setUTCMonth(new_month);
13046             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13047             if (new_month < 0 || new_month > 11)
13048                 new_month = (new_month + 12) % 12;
13049         } else {
13050             // For magnitudes >1, move one month at a time...
13051             for (var i=0; i<mag; i++)
13052                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13053                 new_date = this.moveMonth(new_date, dir);
13054             // ...then reset the day, keeping it in the new month
13055             new_month = new_date.getUTCMonth();
13056             new_date.setUTCDate(day);
13057             test = function(){
13058                 return new_month != new_date.getUTCMonth();
13059             };
13060         }
13061         // Common date-resetting loop -- if date is beyond end of month, make it
13062         // end of month
13063         while (test()){
13064             new_date.setUTCDate(--day);
13065             new_date.setUTCMonth(new_month);
13066         }
13067         return new_date;
13068     },
13069
13070     moveYear: function(date, dir){
13071         return this.moveMonth(date, dir*12);
13072     },
13073
13074     dateWithinRange: function(date){
13075         return date >= this.startDate && date <= this.endDate;
13076     },
13077
13078     
13079     remove: function() {
13080         this.picker().remove();
13081     }
13082    
13083 });
13084
13085 Roo.apply(Roo.bootstrap.DateField,  {
13086     
13087     head : {
13088         tag: 'thead',
13089         cn: [
13090         {
13091             tag: 'tr',
13092             cn: [
13093             {
13094                 tag: 'th',
13095                 cls: 'prev',
13096                 html: '<i class="icon-arrow-left"/>'
13097             },
13098             {
13099                 tag: 'th',
13100                 cls: 'switch',
13101                 colspan: '5'
13102             },
13103             {
13104                 tag: 'th',
13105                 cls: 'next',
13106                 html: '<i class="icon-arrow-right"/>'
13107             }
13108
13109             ]
13110         }
13111         ]
13112     },
13113     
13114     content : {
13115         tag: 'tbody',
13116         cn: [
13117         {
13118             tag: 'tr',
13119             cn: [
13120             {
13121                 tag: 'td',
13122                 colspan: '7'
13123             }
13124             ]
13125         }
13126         ]
13127     },
13128     
13129     footer : {
13130         tag: 'tfoot',
13131         cn: [
13132         {
13133             tag: 'tr',
13134             cn: [
13135             {
13136                 tag: 'th',
13137                 colspan: '7',
13138                 cls: 'today'
13139             }
13140                     
13141             ]
13142         }
13143         ]
13144     },
13145     
13146     dates:{
13147         en: {
13148             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13149             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13150             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13151             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13152             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13153             today: "Today"
13154         }
13155     },
13156     
13157     modes: [
13158     {
13159         clsName: 'days',
13160         navFnc: 'Month',
13161         navStep: 1
13162     },
13163     {
13164         clsName: 'months',
13165         navFnc: 'FullYear',
13166         navStep: 1
13167     },
13168     {
13169         clsName: 'years',
13170         navFnc: 'FullYear',
13171         navStep: 10
13172     }]
13173 });
13174
13175 Roo.apply(Roo.bootstrap.DateField,  {
13176   
13177     template : {
13178         tag: 'div',
13179         cls: 'datepicker dropdown-menu',
13180         cn: [
13181         {
13182             tag: 'div',
13183             cls: 'datepicker-days',
13184             cn: [
13185             {
13186                 tag: 'table',
13187                 cls: 'table-condensed',
13188                 cn:[
13189                 Roo.bootstrap.DateField.head,
13190                 {
13191                     tag: 'tbody'
13192                 },
13193                 Roo.bootstrap.DateField.footer
13194                 ]
13195             }
13196             ]
13197         },
13198         {
13199             tag: 'div',
13200             cls: 'datepicker-months',
13201             cn: [
13202             {
13203                 tag: 'table',
13204                 cls: 'table-condensed',
13205                 cn:[
13206                 Roo.bootstrap.DateField.head,
13207                 Roo.bootstrap.DateField.content,
13208                 Roo.bootstrap.DateField.footer
13209                 ]
13210             }
13211             ]
13212         },
13213         {
13214             tag: 'div',
13215             cls: 'datepicker-years',
13216             cn: [
13217             {
13218                 tag: 'table',
13219                 cls: 'table-condensed',
13220                 cn:[
13221                 Roo.bootstrap.DateField.head,
13222                 Roo.bootstrap.DateField.content,
13223                 Roo.bootstrap.DateField.footer
13224                 ]
13225             }
13226             ]
13227         }
13228         ]
13229     }
13230 });
13231
13232  
13233
13234  /*
13235  * - LGPL
13236  *
13237  * TimeField
13238  * 
13239  */
13240
13241 /**
13242  * @class Roo.bootstrap.TimeField
13243  * @extends Roo.bootstrap.Input
13244  * Bootstrap DateField class
13245  * 
13246  * 
13247  * @constructor
13248  * Create a new TimeField
13249  * @param {Object} config The config object
13250  */
13251
13252 Roo.bootstrap.TimeField = function(config){
13253     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13254     this.addEvents({
13255             /**
13256              * @event show
13257              * Fires when this field show.
13258              * @param {Roo.bootstrap.DateField} this
13259              * @param {Mixed} date The date value
13260              */
13261             show : true,
13262             /**
13263              * @event show
13264              * Fires when this field hide.
13265              * @param {Roo.bootstrap.DateField} this
13266              * @param {Mixed} date The date value
13267              */
13268             hide : true,
13269             /**
13270              * @event select
13271              * Fires when select a date.
13272              * @param {Roo.bootstrap.DateField} this
13273              * @param {Mixed} date The date value
13274              */
13275             select : true
13276         });
13277 };
13278
13279 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13280     
13281     /**
13282      * @cfg {String} format
13283      * The default time format string which can be overriden for localization support.  The format must be
13284      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13285      */
13286     format : "H:i",
13287        
13288     onRender: function(ct, position)
13289     {
13290         
13291         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13292                 
13293         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13294         
13295         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13296         
13297         this.pop = this.picker().select('>.datepicker-time',true).first();
13298         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13299         
13300         this.picker().on('mousedown', this.onMousedown, this);
13301         this.picker().on('click', this.onClick, this);
13302         
13303         this.picker().addClass('datepicker-dropdown');
13304     
13305         this.fillTime();
13306         this.update();
13307             
13308         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13309         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13310         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13311         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13312         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13313         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13314
13315     },
13316     
13317     fireKey: function(e){
13318         if (!this.picker().isVisible()){
13319             if (e.keyCode == 27) // allow escape to hide and re-show picker
13320                 this.show();
13321             return;
13322         }
13323
13324         e.preventDefault();
13325         
13326         switch(e.keyCode){
13327             case 27: // escape
13328                 this.hide();
13329                 break;
13330             case 37: // left
13331             case 39: // right
13332                 this.onTogglePeriod();
13333                 break;
13334             case 38: // up
13335                 this.onIncrementMinutes();
13336                 break;
13337             case 40: // down
13338                 this.onDecrementMinutes();
13339                 break;
13340             case 13: // enter
13341             case 9: // tab
13342                 this.setTime();
13343                 break;
13344         }
13345     },
13346     
13347     onClick: function(e) {
13348         e.stopPropagation();
13349         e.preventDefault();
13350     },
13351     
13352     picker : function()
13353     {
13354         return this.el.select('.datepicker', true).first();
13355     },
13356     
13357     fillTime: function()
13358     {    
13359         var time = this.pop.select('tbody', true).first();
13360         
13361         time.dom.innerHTML = '';
13362         
13363         time.createChild({
13364             tag: 'tr',
13365             cn: [
13366                 {
13367                     tag: 'td',
13368                     cn: [
13369                         {
13370                             tag: 'a',
13371                             href: '#',
13372                             cls: 'btn',
13373                             cn: [
13374                                 {
13375                                     tag: 'span',
13376                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13377                                 }
13378                             ]
13379                         } 
13380                     ]
13381                 },
13382                 {
13383                     tag: 'td',
13384                     cls: 'separator'
13385                 },
13386                 {
13387                     tag: 'td',
13388                     cn: [
13389                         {
13390                             tag: 'a',
13391                             href: '#',
13392                             cls: 'btn',
13393                             cn: [
13394                                 {
13395                                     tag: 'span',
13396                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13397                                 }
13398                             ]
13399                         }
13400                     ]
13401                 },
13402                 {
13403                     tag: 'td',
13404                     cls: 'separator'
13405                 }
13406             ]
13407         });
13408         
13409         time.createChild({
13410             tag: 'tr',
13411             cn: [
13412                 {
13413                     tag: 'td',
13414                     cn: [
13415                         {
13416                             tag: 'span',
13417                             cls: 'timepicker-hour',
13418                             html: '00'
13419                         }  
13420                     ]
13421                 },
13422                 {
13423                     tag: 'td',
13424                     cls: 'separator',
13425                     html: ':'
13426                 },
13427                 {
13428                     tag: 'td',
13429                     cn: [
13430                         {
13431                             tag: 'span',
13432                             cls: 'timepicker-minute',
13433                             html: '00'
13434                         }  
13435                     ]
13436                 },
13437                 {
13438                     tag: 'td',
13439                     cls: 'separator'
13440                 },
13441                 {
13442                     tag: 'td',
13443                     cn: [
13444                         {
13445                             tag: 'button',
13446                             type: 'button',
13447                             cls: 'btn btn-primary period',
13448                             html: 'AM'
13449                             
13450                         }
13451                     ]
13452                 }
13453             ]
13454         });
13455         
13456         time.createChild({
13457             tag: 'tr',
13458             cn: [
13459                 {
13460                     tag: 'td',
13461                     cn: [
13462                         {
13463                             tag: 'a',
13464                             href: '#',
13465                             cls: 'btn',
13466                             cn: [
13467                                 {
13468                                     tag: 'span',
13469                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13470                                 }
13471                             ]
13472                         }
13473                     ]
13474                 },
13475                 {
13476                     tag: 'td',
13477                     cls: 'separator'
13478                 },
13479                 {
13480                     tag: 'td',
13481                     cn: [
13482                         {
13483                             tag: 'a',
13484                             href: '#',
13485                             cls: 'btn',
13486                             cn: [
13487                                 {
13488                                     tag: 'span',
13489                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13490                                 }
13491                             ]
13492                         }
13493                     ]
13494                 },
13495                 {
13496                     tag: 'td',
13497                     cls: 'separator'
13498                 }
13499             ]
13500         });
13501         
13502     },
13503     
13504     update: function()
13505     {
13506         
13507         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13508         
13509         this.fill();
13510     },
13511     
13512     fill: function() 
13513     {
13514         var hours = this.time.getHours();
13515         var minutes = this.time.getMinutes();
13516         var period = 'AM';
13517         
13518         if(hours > 11){
13519             period = 'PM';
13520         }
13521         
13522         if(hours == 0){
13523             hours = 12;
13524         }
13525         
13526         
13527         if(hours > 12){
13528             hours = hours - 12;
13529         }
13530         
13531         if(hours < 10){
13532             hours = '0' + hours;
13533         }
13534         
13535         if(minutes < 10){
13536             minutes = '0' + minutes;
13537         }
13538         
13539         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13540         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13541         this.pop.select('button', true).first().dom.innerHTML = period;
13542         
13543     },
13544     
13545     place: function()
13546     {   
13547         this.picker().removeClass(['bottom', 'top']);
13548         
13549         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13550             /*
13551              * place to the top of element!
13552              *
13553              */
13554             
13555             this.picker().addClass('top');
13556             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13557             
13558             return;
13559         }
13560         
13561         this.picker().addClass('bottom');
13562         
13563         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13564     },
13565   
13566     onFocus : function()
13567     {
13568         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13569         this.show();
13570     },
13571     
13572     onBlur : function()
13573     {
13574         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13575         this.hide();
13576     },
13577     
13578     show : function()
13579     {
13580         this.picker().show();
13581         this.pop.show();
13582         this.update();
13583         this.place();
13584         
13585         this.fireEvent('show', this, this.date);
13586     },
13587     
13588     hide : function()
13589     {
13590         this.picker().hide();
13591         this.pop.hide();
13592         
13593         this.fireEvent('hide', this, this.date);
13594     },
13595     
13596     setTime : function()
13597     {
13598         this.hide();
13599         this.setValue(this.time.format(this.format));
13600         
13601         this.fireEvent('select', this, this.date);
13602         
13603         
13604     },
13605     
13606     onMousedown: function(e){
13607         e.stopPropagation();
13608         e.preventDefault();
13609     },
13610     
13611     onIncrementHours: function()
13612     {
13613         Roo.log('onIncrementHours');
13614         this.time = this.time.add(Date.HOUR, 1);
13615         this.update();
13616         
13617     },
13618     
13619     onDecrementHours: function()
13620     {
13621         Roo.log('onDecrementHours');
13622         this.time = this.time.add(Date.HOUR, -1);
13623         this.update();
13624     },
13625     
13626     onIncrementMinutes: function()
13627     {
13628         Roo.log('onIncrementMinutes');
13629         this.time = this.time.add(Date.MINUTE, 1);
13630         this.update();
13631     },
13632     
13633     onDecrementMinutes: function()
13634     {
13635         Roo.log('onDecrementMinutes');
13636         this.time = this.time.add(Date.MINUTE, -1);
13637         this.update();
13638     },
13639     
13640     onTogglePeriod: function()
13641     {
13642         Roo.log('onTogglePeriod');
13643         this.time = this.time.add(Date.HOUR, 12);
13644         this.update();
13645     }
13646     
13647    
13648 });
13649
13650 Roo.apply(Roo.bootstrap.TimeField,  {
13651     
13652     content : {
13653         tag: 'tbody',
13654         cn: [
13655             {
13656                 tag: 'tr',
13657                 cn: [
13658                 {
13659                     tag: 'td',
13660                     colspan: '7'
13661                 }
13662                 ]
13663             }
13664         ]
13665     },
13666     
13667     footer : {
13668         tag: 'tfoot',
13669         cn: [
13670             {
13671                 tag: 'tr',
13672                 cn: [
13673                 {
13674                     tag: 'th',
13675                     colspan: '7',
13676                     cls: '',
13677                     cn: [
13678                         {
13679                             tag: 'button',
13680                             cls: 'btn btn-info ok',
13681                             html: 'OK'
13682                         }
13683                     ]
13684                 }
13685
13686                 ]
13687             }
13688         ]
13689     }
13690 });
13691
13692 Roo.apply(Roo.bootstrap.TimeField,  {
13693   
13694     template : {
13695         tag: 'div',
13696         cls: 'datepicker dropdown-menu',
13697         cn: [
13698             {
13699                 tag: 'div',
13700                 cls: 'datepicker-time',
13701                 cn: [
13702                 {
13703                     tag: 'table',
13704                     cls: 'table-condensed',
13705                     cn:[
13706                     Roo.bootstrap.TimeField.content,
13707                     Roo.bootstrap.TimeField.footer
13708                     ]
13709                 }
13710                 ]
13711             }
13712         ]
13713     }
13714 });
13715
13716  
13717
13718  /*
13719  * - LGPL
13720  *
13721  * CheckBox
13722  * 
13723  */
13724
13725 /**
13726  * @class Roo.bootstrap.CheckBox
13727  * @extends Roo.bootstrap.Input
13728  * Bootstrap CheckBox class
13729  * 
13730  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13731  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13732  * @cfg {String} boxLabel The text that appears beside the checkbox
13733  * @cfg {Boolean} checked initnal the element
13734  * 
13735  * @constructor
13736  * Create a new CheckBox
13737  * @param {Object} config The config object
13738  */
13739
13740 Roo.bootstrap.CheckBox = function(config){
13741     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13742    
13743         this.addEvents({
13744             /**
13745             * @event check
13746             * Fires when the element is checked or unchecked.
13747             * @param {Roo.bootstrap.CheckBox} this This input
13748             * @param {Boolean} checked The new checked value
13749             */
13750            check : true
13751         });
13752 };
13753
13754 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13755     
13756     inputType: 'checkbox',
13757     inputValue: 1,
13758     valueOff: 0,
13759     boxLabel: false,
13760     checked: false,
13761     
13762     getAutoCreate : function()
13763     {
13764         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13765         
13766         var id = Roo.id();
13767         
13768         var cfg = {};
13769         
13770         cfg.cls = 'form-group' //input-group
13771         
13772         var input =  {
13773             tag: 'input',
13774             id : id,
13775             type : this.inputType,
13776             value : (!this.checked) ? this.valueOff : this.inputValue,
13777             cls : 'form-box',
13778             placeholder : this.placeholder || ''
13779             
13780         };
13781         
13782         if (this.disabled) {
13783             input.disabled=true;
13784         }
13785         
13786         if(this.checked){
13787             input.checked = this.checked;
13788         }
13789         
13790         if (this.name) {
13791             input.name = this.name;
13792         }
13793         
13794         if (this.size) {
13795             input.cls += ' input-' + this.size;
13796         }
13797         
13798         var settings=this;
13799         ['xs','sm','md','lg'].map(function(size){
13800             if (settings[size]) {
13801                 cfg.cls += ' col-' + size + '-' + settings[size];
13802             }
13803         });
13804         
13805         var inputblock = input;
13806         
13807         if (this.before || this.after) {
13808             
13809             inputblock = {
13810                 cls : 'input-group',
13811                 cn :  [] 
13812             };
13813             if (this.before) {
13814                 inputblock.cn.push({
13815                     tag :'span',
13816                     cls : 'input-group-addon',
13817                     html : this.before
13818                 });
13819             }
13820             inputblock.cn.push(input);
13821             if (this.after) {
13822                 inputblock.cn.push({
13823                     tag :'span',
13824                     cls : 'input-group-addon',
13825                     html : this.after
13826                 });
13827             }
13828             
13829         };
13830         
13831         if (align ==='left' && this.fieldLabel.length) {
13832                 Roo.log("left and has label");
13833                 cfg.cn = [
13834                     
13835                     {
13836                         tag: 'label',
13837                         'for' :  id,
13838                         cls : 'control-label col-md-' + this.labelWidth,
13839                         html : this.fieldLabel
13840                         
13841                     },
13842                     {
13843                         cls : "col-md-" + (12 - this.labelWidth), 
13844                         cn: [
13845                             inputblock
13846                         ]
13847                     }
13848                     
13849                 ];
13850         } else if ( this.fieldLabel.length) {
13851                 Roo.log(" label");
13852                 cfg.cn = [
13853                    
13854                     {
13855                         tag: this.boxLabel ? 'span' : 'label',
13856                         'for': id,
13857                         cls: 'control-label box-input-label',
13858                         //cls : 'input-group-addon',
13859                         html : this.fieldLabel
13860                         
13861                     },
13862                     
13863                     inputblock
13864                     
13865                 ];
13866
13867         } else {
13868             
13869                    Roo.log(" no label && no align");
13870                 cfg.cn = [
13871                     
13872                         inputblock
13873                     
13874                 ];
13875                 
13876                 
13877         };
13878         
13879         if(this.boxLabel){
13880             cfg.cn.push({
13881                 tag: 'label',
13882                 'for': id,
13883                 cls: 'box-label',
13884                 html: this.boxLabel
13885             })
13886         }
13887         
13888         return cfg;
13889         
13890     },
13891     
13892     /**
13893      * return the real input element.
13894      */
13895     inputEl: function ()
13896     {
13897         return this.el.select('input.form-box',true).first();
13898     },
13899     
13900     label: function()
13901     {
13902         return this.el.select('label.control-label',true).first();
13903     },
13904     
13905     initEvents : function()
13906     {
13907 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13908         
13909         this.inputEl().on('click', this.onClick,  this);
13910         
13911     },
13912     
13913     onClick : function()
13914     {   
13915         this.setChecked(!this.checked);
13916     },
13917     
13918     setChecked : function(state,suppressEvent)
13919     {
13920         this.checked = state;
13921         
13922         this.inputEl().dom.checked = state;
13923         
13924         if(suppressEvent !== true){
13925             this.fireEvent('check', this, state);
13926         }
13927         
13928         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13929         
13930     },
13931     
13932     setValue : function(v,suppressEvent)
13933     {
13934         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13935     }
13936     
13937 });
13938
13939  
13940 /*
13941  * - LGPL
13942  *
13943  * Radio
13944  * 
13945  */
13946
13947 /**
13948  * @class Roo.bootstrap.Radio
13949  * @extends Roo.bootstrap.CheckBox
13950  * Bootstrap Radio class
13951
13952  * @constructor
13953  * Create a new Radio
13954  * @param {Object} config The config object
13955  */
13956
13957 Roo.bootstrap.Radio = function(config){
13958     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13959    
13960 };
13961
13962 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13963     
13964     inputType: 'radio',
13965     inputValue: '',
13966     valueOff: '',
13967     
13968     getAutoCreate : function()
13969     {
13970         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13971         
13972         var id = Roo.id();
13973         
13974         var cfg = {};
13975         
13976         cfg.cls = 'form-group' //input-group
13977         
13978         var input =  {
13979             tag: 'input',
13980             id : id,
13981             type : this.inputType,
13982             value : (!this.checked) ? this.valueOff : this.inputValue,
13983             cls : 'form-box',
13984             placeholder : this.placeholder || ''
13985             
13986         };
13987         
13988         if (this.disabled) {
13989             input.disabled=true;
13990         }
13991         
13992         if(this.checked){
13993             input.checked = this.checked;
13994         }
13995         
13996         if (this.name) {
13997             input.name = this.name;
13998         }
13999         
14000         if (this.size) {
14001             input.cls += ' input-' + this.size;
14002         }
14003         
14004         var settings=this;
14005         ['xs','sm','md','lg'].map(function(size){
14006             if (settings[size]) {
14007                 cfg.cls += ' col-' + size + '-' + settings[size];
14008             }
14009         });
14010         
14011         var inputblock = input;
14012         
14013         if (this.before || this.after) {
14014             
14015             inputblock = {
14016                 cls : 'input-group',
14017                 cn :  [] 
14018             };
14019             if (this.before) {
14020                 inputblock.cn.push({
14021                     tag :'span',
14022                     cls : 'input-group-addon',
14023                     html : this.before
14024                 });
14025             }
14026             inputblock.cn.push(input);
14027             if (this.after) {
14028                 inputblock.cn.push({
14029                     tag :'span',
14030                     cls : 'input-group-addon',
14031                     html : this.after
14032                 });
14033             }
14034             
14035         };
14036         
14037         if (align ==='left' && this.fieldLabel.length) {
14038                 Roo.log("left and has label");
14039                 cfg.cn = [
14040                     
14041                     {
14042                         tag: 'label',
14043                         'for' :  id,
14044                         cls : 'control-label col-md-' + this.labelWidth,
14045                         html : this.fieldLabel
14046                         
14047                     },
14048                     {
14049                         cls : "col-md-" + (12 - this.labelWidth), 
14050                         cn: [
14051                             inputblock
14052                         ]
14053                     }
14054                     
14055                 ];
14056         } else if ( this.fieldLabel.length) {
14057                 Roo.log(" label");
14058                  cfg.cn = [
14059                    
14060                     {
14061                         tag: 'label',
14062                         'for': id,
14063                         cls: 'control-label box-input-label',
14064                         //cls : 'input-group-addon',
14065                         html : this.fieldLabel
14066                         
14067                     },
14068                     
14069                     inputblock
14070                     
14071                 ];
14072
14073         } else {
14074             
14075                    Roo.log(" no label && no align");
14076                 cfg.cn = [
14077                     
14078                         inputblock
14079                     
14080                 ];
14081                 
14082                 
14083         };
14084         
14085         if(this.boxLabel){
14086             cfg.cn.push({
14087                 tag: 'label',
14088                 'for': id,
14089                 cls: 'box-label',
14090                 html: this.boxLabel
14091             })
14092         }
14093         
14094         return cfg;
14095         
14096     },
14097    
14098     onClick : function()
14099     {   
14100         this.setChecked(true);
14101     },
14102     
14103     setChecked : function(state,suppressEvent)
14104     {
14105         if(state){
14106             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14107                 v.dom.checked = false;
14108             });
14109         }
14110         
14111         this.checked = state;
14112         this.inputEl().dom.checked = state;
14113         
14114         if(suppressEvent !== true){
14115             this.fireEvent('check', this, state);
14116         }
14117         
14118         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14119         
14120     },
14121     
14122     getGroupValue : function()
14123     {
14124         var value = ''
14125         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14126             if(v.dom.checked == true){
14127                 value = v.dom.value;
14128             }
14129         });
14130         
14131         return value;
14132     },
14133     
14134     /**
14135      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14136      * @return {Mixed} value The field value
14137      */
14138     getValue : function(){
14139         return this.getGroupValue();
14140     }
14141     
14142 });
14143
14144  
14145 //<script type="text/javascript">
14146
14147 /*
14148  * Based  Ext JS Library 1.1.1
14149  * Copyright(c) 2006-2007, Ext JS, LLC.
14150  * LGPL
14151  *
14152  */
14153  
14154 /**
14155  * @class Roo.HtmlEditorCore
14156  * @extends Roo.Component
14157  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14158  *
14159  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14160  */
14161
14162 Roo.HtmlEditorCore = function(config){
14163     
14164     
14165     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14166     this.addEvents({
14167         /**
14168          * @event initialize
14169          * Fires when the editor is fully initialized (including the iframe)
14170          * @param {Roo.HtmlEditorCore} this
14171          */
14172         initialize: true,
14173         /**
14174          * @event activate
14175          * Fires when the editor is first receives the focus. Any insertion must wait
14176          * until after this event.
14177          * @param {Roo.HtmlEditorCore} this
14178          */
14179         activate: true,
14180          /**
14181          * @event beforesync
14182          * Fires before the textarea is updated with content from the editor iframe. Return false
14183          * to cancel the sync.
14184          * @param {Roo.HtmlEditorCore} this
14185          * @param {String} html
14186          */
14187         beforesync: true,
14188          /**
14189          * @event beforepush
14190          * Fires before the iframe editor is updated with content from the textarea. Return false
14191          * to cancel the push.
14192          * @param {Roo.HtmlEditorCore} this
14193          * @param {String} html
14194          */
14195         beforepush: true,
14196          /**
14197          * @event sync
14198          * Fires when the textarea is updated with content from the editor iframe.
14199          * @param {Roo.HtmlEditorCore} this
14200          * @param {String} html
14201          */
14202         sync: true,
14203          /**
14204          * @event push
14205          * Fires when the iframe editor is updated with content from the textarea.
14206          * @param {Roo.HtmlEditorCore} this
14207          * @param {String} html
14208          */
14209         push: true,
14210         
14211         /**
14212          * @event editorevent
14213          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14214          * @param {Roo.HtmlEditorCore} this
14215          */
14216         editorevent: true
14217     });
14218      
14219 };
14220
14221
14222 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14223
14224
14225      /**
14226      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14227      */
14228     
14229     owner : false,
14230     
14231      /**
14232      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14233      *                        Roo.resizable.
14234      */
14235     resizable : false,
14236      /**
14237      * @cfg {Number} height (in pixels)
14238      */   
14239     height: 300,
14240    /**
14241      * @cfg {Number} width (in pixels)
14242      */   
14243     width: 500,
14244     
14245     /**
14246      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14247      * 
14248      */
14249     stylesheets: false,
14250     
14251     // id of frame..
14252     frameId: false,
14253     
14254     // private properties
14255     validationEvent : false,
14256     deferHeight: true,
14257     initialized : false,
14258     activated : false,
14259     sourceEditMode : false,
14260     onFocus : Roo.emptyFn,
14261     iframePad:3,
14262     hideMode:'offsets',
14263     
14264     clearUp: true,
14265     
14266      
14267     
14268
14269     /**
14270      * Protected method that will not generally be called directly. It
14271      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14272      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14273      */
14274     getDocMarkup : function(){
14275         // body styles..
14276         var st = '';
14277         Roo.log(this.stylesheets);
14278         
14279         // inherit styels from page...?? 
14280         if (this.stylesheets === false) {
14281             
14282             Roo.get(document.head).select('style').each(function(node) {
14283                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14284             });
14285             
14286             Roo.get(document.head).select('link').each(function(node) { 
14287                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14288             });
14289             
14290         } else if (!this.stylesheets.length) {
14291                 // simple..
14292                 st = '<style type="text/css">' +
14293                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14294                    '</style>';
14295         } else {
14296             Roo.each(this.stylesheets, function(s) {
14297                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14298             });
14299             
14300         }
14301         
14302         st +=  '<style type="text/css">' +
14303             'IMG { cursor: pointer } ' +
14304         '</style>';
14305
14306         
14307         return '<html><head>' + st  +
14308             //<style type="text/css">' +
14309             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14310             //'</style>' +
14311             ' </head><body class="roo-htmleditor-body"></body></html>';
14312     },
14313
14314     // private
14315     onRender : function(ct, position)
14316     {
14317         var _t = this;
14318         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14319         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14320         
14321         
14322         this.el.dom.style.border = '0 none';
14323         this.el.dom.setAttribute('tabIndex', -1);
14324         this.el.addClass('x-hidden hide');
14325         
14326         
14327         
14328         if(Roo.isIE){ // fix IE 1px bogus margin
14329             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14330         }
14331        
14332         
14333         this.frameId = Roo.id();
14334         
14335          
14336         
14337         var iframe = this.owner.wrap.createChild({
14338             tag: 'iframe',
14339             cls: 'form-control', // bootstrap..
14340             id: this.frameId,
14341             name: this.frameId,
14342             frameBorder : 'no',
14343             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14344         }, this.el
14345         );
14346         
14347         
14348         this.iframe = iframe.dom;
14349
14350          this.assignDocWin();
14351         
14352         this.doc.designMode = 'on';
14353        
14354         this.doc.open();
14355         this.doc.write(this.getDocMarkup());
14356         this.doc.close();
14357
14358         
14359         var task = { // must defer to wait for browser to be ready
14360             run : function(){
14361                 //console.log("run task?" + this.doc.readyState);
14362                 this.assignDocWin();
14363                 if(this.doc.body || this.doc.readyState == 'complete'){
14364                     try {
14365                         this.doc.designMode="on";
14366                     } catch (e) {
14367                         return;
14368                     }
14369                     Roo.TaskMgr.stop(task);
14370                     this.initEditor.defer(10, this);
14371                 }
14372             },
14373             interval : 10,
14374             duration: 10000,
14375             scope: this
14376         };
14377         Roo.TaskMgr.start(task);
14378
14379         
14380          
14381     },
14382
14383     // private
14384     onResize : function(w, h)
14385     {
14386          Roo.log('resize: ' +w + ',' + h );
14387         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14388         if(!this.iframe){
14389             return;
14390         }
14391         if(typeof w == 'number'){
14392             
14393             this.iframe.style.width = w + 'px';
14394         }
14395         if(typeof h == 'number'){
14396             
14397             this.iframe.style.height = h + 'px';
14398             if(this.doc){
14399                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14400             }
14401         }
14402         
14403     },
14404
14405     /**
14406      * Toggles the editor between standard and source edit mode.
14407      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14408      */
14409     toggleSourceEdit : function(sourceEditMode){
14410         
14411         this.sourceEditMode = sourceEditMode === true;
14412         
14413         if(this.sourceEditMode){
14414  
14415             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14416             
14417         }else{
14418             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14419             //this.iframe.className = '';
14420             this.deferFocus();
14421         }
14422         //this.setSize(this.owner.wrap.getSize());
14423         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14424     },
14425
14426     
14427   
14428
14429     /**
14430      * Protected method that will not generally be called directly. If you need/want
14431      * custom HTML cleanup, this is the method you should override.
14432      * @param {String} html The HTML to be cleaned
14433      * return {String} The cleaned HTML
14434      */
14435     cleanHtml : function(html){
14436         html = String(html);
14437         if(html.length > 5){
14438             if(Roo.isSafari){ // strip safari nonsense
14439                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14440             }
14441         }
14442         if(html == '&nbsp;'){
14443             html = '';
14444         }
14445         return html;
14446     },
14447
14448     /**
14449      * HTML Editor -> Textarea
14450      * Protected method that will not generally be called directly. Syncs the contents
14451      * of the editor iframe with the textarea.
14452      */
14453     syncValue : function(){
14454         if(this.initialized){
14455             var bd = (this.doc.body || this.doc.documentElement);
14456             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14457             var html = bd.innerHTML;
14458             if(Roo.isSafari){
14459                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14460                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14461                 if(m && m[1]){
14462                     html = '<div style="'+m[0]+'">' + html + '</div>';
14463                 }
14464             }
14465             html = this.cleanHtml(html);
14466             // fix up the special chars.. normaly like back quotes in word...
14467             // however we do not want to do this with chinese..
14468             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14469                 var cc = b.charCodeAt();
14470                 if (
14471                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14472                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14473                     (cc >= 0xf900 && cc < 0xfb00 )
14474                 ) {
14475                         return b;
14476                 }
14477                 return "&#"+cc+";" 
14478             });
14479             if(this.owner.fireEvent('beforesync', this, html) !== false){
14480                 this.el.dom.value = html;
14481                 this.owner.fireEvent('sync', this, html);
14482             }
14483         }
14484     },
14485
14486     /**
14487      * Protected method that will not generally be called directly. Pushes the value of the textarea
14488      * into the iframe editor.
14489      */
14490     pushValue : function(){
14491         if(this.initialized){
14492             var v = this.el.dom.value.trim();
14493             
14494 //            if(v.length < 1){
14495 //                v = '&#160;';
14496 //            }
14497             
14498             if(this.owner.fireEvent('beforepush', this, v) !== false){
14499                 var d = (this.doc.body || this.doc.documentElement);
14500                 d.innerHTML = v;
14501                 this.cleanUpPaste();
14502                 this.el.dom.value = d.innerHTML;
14503                 this.owner.fireEvent('push', this, v);
14504             }
14505         }
14506     },
14507
14508     // private
14509     deferFocus : function(){
14510         this.focus.defer(10, this);
14511     },
14512
14513     // doc'ed in Field
14514     focus : function(){
14515         if(this.win && !this.sourceEditMode){
14516             this.win.focus();
14517         }else{
14518             this.el.focus();
14519         }
14520     },
14521     
14522     assignDocWin: function()
14523     {
14524         var iframe = this.iframe;
14525         
14526          if(Roo.isIE){
14527             this.doc = iframe.contentWindow.document;
14528             this.win = iframe.contentWindow;
14529         } else {
14530             if (!Roo.get(this.frameId)) {
14531                 return;
14532             }
14533             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14534             this.win = Roo.get(this.frameId).dom.contentWindow;
14535         }
14536     },
14537     
14538     // private
14539     initEditor : function(){
14540         //console.log("INIT EDITOR");
14541         this.assignDocWin();
14542         
14543         
14544         
14545         this.doc.designMode="on";
14546         this.doc.open();
14547         this.doc.write(this.getDocMarkup());
14548         this.doc.close();
14549         
14550         var dbody = (this.doc.body || this.doc.documentElement);
14551         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14552         // this copies styles from the containing element into thsi one..
14553         // not sure why we need all of this..
14554         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14555         ss['background-attachment'] = 'fixed'; // w3c
14556         dbody.bgProperties = 'fixed'; // ie
14557         Roo.DomHelper.applyStyles(dbody, ss);
14558         Roo.EventManager.on(this.doc, {
14559             //'mousedown': this.onEditorEvent,
14560             'mouseup': this.onEditorEvent,
14561             'dblclick': this.onEditorEvent,
14562             'click': this.onEditorEvent,
14563             'keyup': this.onEditorEvent,
14564             buffer:100,
14565             scope: this
14566         });
14567         if(Roo.isGecko){
14568             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14569         }
14570         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14571             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14572         }
14573         this.initialized = true;
14574
14575         this.owner.fireEvent('initialize', this);
14576         this.pushValue();
14577     },
14578
14579     // private
14580     onDestroy : function(){
14581         
14582         
14583         
14584         if(this.rendered){
14585             
14586             //for (var i =0; i < this.toolbars.length;i++) {
14587             //    // fixme - ask toolbars for heights?
14588             //    this.toolbars[i].onDestroy();
14589            // }
14590             
14591             //this.wrap.dom.innerHTML = '';
14592             //this.wrap.remove();
14593         }
14594     },
14595
14596     // private
14597     onFirstFocus : function(){
14598         
14599         this.assignDocWin();
14600         
14601         
14602         this.activated = true;
14603          
14604     
14605         if(Roo.isGecko){ // prevent silly gecko errors
14606             this.win.focus();
14607             var s = this.win.getSelection();
14608             if(!s.focusNode || s.focusNode.nodeType != 3){
14609                 var r = s.getRangeAt(0);
14610                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14611                 r.collapse(true);
14612                 this.deferFocus();
14613             }
14614             try{
14615                 this.execCmd('useCSS', true);
14616                 this.execCmd('styleWithCSS', false);
14617             }catch(e){}
14618         }
14619         this.owner.fireEvent('activate', this);
14620     },
14621
14622     // private
14623     adjustFont: function(btn){
14624         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14625         //if(Roo.isSafari){ // safari
14626         //    adjust *= 2;
14627        // }
14628         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14629         if(Roo.isSafari){ // safari
14630             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14631             v =  (v < 10) ? 10 : v;
14632             v =  (v > 48) ? 48 : v;
14633             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14634             
14635         }
14636         
14637         
14638         v = Math.max(1, v+adjust);
14639         
14640         this.execCmd('FontSize', v  );
14641     },
14642
14643     onEditorEvent : function(e){
14644         this.owner.fireEvent('editorevent', this, e);
14645       //  this.updateToolbar();
14646         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14647     },
14648
14649     insertTag : function(tg)
14650     {
14651         // could be a bit smarter... -> wrap the current selected tRoo..
14652         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14653             
14654             range = this.createRange(this.getSelection());
14655             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14656             wrappingNode.appendChild(range.extractContents());
14657             range.insertNode(wrappingNode);
14658
14659             return;
14660             
14661             
14662             
14663         }
14664         this.execCmd("formatblock",   tg);
14665         
14666     },
14667     
14668     insertText : function(txt)
14669     {
14670         
14671         
14672         var range = this.createRange();
14673         range.deleteContents();
14674                //alert(Sender.getAttribute('label'));
14675                
14676         range.insertNode(this.doc.createTextNode(txt));
14677     } ,
14678     
14679      
14680
14681     /**
14682      * Executes a Midas editor command on the editor document and performs necessary focus and
14683      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14684      * @param {String} cmd The Midas command
14685      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14686      */
14687     relayCmd : function(cmd, value){
14688         this.win.focus();
14689         this.execCmd(cmd, value);
14690         this.owner.fireEvent('editorevent', this);
14691         //this.updateToolbar();
14692         this.owner.deferFocus();
14693     },
14694
14695     /**
14696      * Executes a Midas editor command directly on the editor document.
14697      * For visual commands, you should use {@link #relayCmd} instead.
14698      * <b>This should only be called after the editor is initialized.</b>
14699      * @param {String} cmd The Midas command
14700      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14701      */
14702     execCmd : function(cmd, value){
14703         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14704         this.syncValue();
14705     },
14706  
14707  
14708    
14709     /**
14710      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14711      * to insert tRoo.
14712      * @param {String} text | dom node.. 
14713      */
14714     insertAtCursor : function(text)
14715     {
14716         
14717         
14718         
14719         if(!this.activated){
14720             return;
14721         }
14722         /*
14723         if(Roo.isIE){
14724             this.win.focus();
14725             var r = this.doc.selection.createRange();
14726             if(r){
14727                 r.collapse(true);
14728                 r.pasteHTML(text);
14729                 this.syncValue();
14730                 this.deferFocus();
14731             
14732             }
14733             return;
14734         }
14735         */
14736         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14737             this.win.focus();
14738             
14739             
14740             // from jquery ui (MIT licenced)
14741             var range, node;
14742             var win = this.win;
14743             
14744             if (win.getSelection && win.getSelection().getRangeAt) {
14745                 range = win.getSelection().getRangeAt(0);
14746                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14747                 range.insertNode(node);
14748             } else if (win.document.selection && win.document.selection.createRange) {
14749                 // no firefox support
14750                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14751                 win.document.selection.createRange().pasteHTML(txt);
14752             } else {
14753                 // no firefox support
14754                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14755                 this.execCmd('InsertHTML', txt);
14756             } 
14757             
14758             this.syncValue();
14759             
14760             this.deferFocus();
14761         }
14762     },
14763  // private
14764     mozKeyPress : function(e){
14765         if(e.ctrlKey){
14766             var c = e.getCharCode(), cmd;
14767           
14768             if(c > 0){
14769                 c = String.fromCharCode(c).toLowerCase();
14770                 switch(c){
14771                     case 'b':
14772                         cmd = 'bold';
14773                         break;
14774                     case 'i':
14775                         cmd = 'italic';
14776                         break;
14777                     
14778                     case 'u':
14779                         cmd = 'underline';
14780                         break;
14781                     
14782                     case 'v':
14783                         this.cleanUpPaste.defer(100, this);
14784                         return;
14785                         
14786                 }
14787                 if(cmd){
14788                     this.win.focus();
14789                     this.execCmd(cmd);
14790                     this.deferFocus();
14791                     e.preventDefault();
14792                 }
14793                 
14794             }
14795         }
14796     },
14797
14798     // private
14799     fixKeys : function(){ // load time branching for fastest keydown performance
14800         if(Roo.isIE){
14801             return function(e){
14802                 var k = e.getKey(), r;
14803                 if(k == e.TAB){
14804                     e.stopEvent();
14805                     r = this.doc.selection.createRange();
14806                     if(r){
14807                         r.collapse(true);
14808                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14809                         this.deferFocus();
14810                     }
14811                     return;
14812                 }
14813                 
14814                 if(k == e.ENTER){
14815                     r = this.doc.selection.createRange();
14816                     if(r){
14817                         var target = r.parentElement();
14818                         if(!target || target.tagName.toLowerCase() != 'li'){
14819                             e.stopEvent();
14820                             r.pasteHTML('<br />');
14821                             r.collapse(false);
14822                             r.select();
14823                         }
14824                     }
14825                 }
14826                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14827                     this.cleanUpPaste.defer(100, this);
14828                     return;
14829                 }
14830                 
14831                 
14832             };
14833         }else if(Roo.isOpera){
14834             return function(e){
14835                 var k = e.getKey();
14836                 if(k == e.TAB){
14837                     e.stopEvent();
14838                     this.win.focus();
14839                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14840                     this.deferFocus();
14841                 }
14842                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14843                     this.cleanUpPaste.defer(100, this);
14844                     return;
14845                 }
14846                 
14847             };
14848         }else if(Roo.isSafari){
14849             return function(e){
14850                 var k = e.getKey();
14851                 
14852                 if(k == e.TAB){
14853                     e.stopEvent();
14854                     this.execCmd('InsertText','\t');
14855                     this.deferFocus();
14856                     return;
14857                 }
14858                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14859                     this.cleanUpPaste.defer(100, this);
14860                     return;
14861                 }
14862                 
14863              };
14864         }
14865     }(),
14866     
14867     getAllAncestors: function()
14868     {
14869         var p = this.getSelectedNode();
14870         var a = [];
14871         if (!p) {
14872             a.push(p); // push blank onto stack..
14873             p = this.getParentElement();
14874         }
14875         
14876         
14877         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14878             a.push(p);
14879             p = p.parentNode;
14880         }
14881         a.push(this.doc.body);
14882         return a;
14883     },
14884     lastSel : false,
14885     lastSelNode : false,
14886     
14887     
14888     getSelection : function() 
14889     {
14890         this.assignDocWin();
14891         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14892     },
14893     
14894     getSelectedNode: function() 
14895     {
14896         // this may only work on Gecko!!!
14897         
14898         // should we cache this!!!!
14899         
14900         
14901         
14902          
14903         var range = this.createRange(this.getSelection()).cloneRange();
14904         
14905         if (Roo.isIE) {
14906             var parent = range.parentElement();
14907             while (true) {
14908                 var testRange = range.duplicate();
14909                 testRange.moveToElementText(parent);
14910                 if (testRange.inRange(range)) {
14911                     break;
14912                 }
14913                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14914                     break;
14915                 }
14916                 parent = parent.parentElement;
14917             }
14918             return parent;
14919         }
14920         
14921         // is ancestor a text element.
14922         var ac =  range.commonAncestorContainer;
14923         if (ac.nodeType == 3) {
14924             ac = ac.parentNode;
14925         }
14926         
14927         var ar = ac.childNodes;
14928          
14929         var nodes = [];
14930         var other_nodes = [];
14931         var has_other_nodes = false;
14932         for (var i=0;i<ar.length;i++) {
14933             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14934                 continue;
14935             }
14936             // fullly contained node.
14937             
14938             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14939                 nodes.push(ar[i]);
14940                 continue;
14941             }
14942             
14943             // probably selected..
14944             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14945                 other_nodes.push(ar[i]);
14946                 continue;
14947             }
14948             // outer..
14949             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14950                 continue;
14951             }
14952             
14953             
14954             has_other_nodes = true;
14955         }
14956         if (!nodes.length && other_nodes.length) {
14957             nodes= other_nodes;
14958         }
14959         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14960             return false;
14961         }
14962         
14963         return nodes[0];
14964     },
14965     createRange: function(sel)
14966     {
14967         // this has strange effects when using with 
14968         // top toolbar - not sure if it's a great idea.
14969         //this.editor.contentWindow.focus();
14970         if (typeof sel != "undefined") {
14971             try {
14972                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14973             } catch(e) {
14974                 return this.doc.createRange();
14975             }
14976         } else {
14977             return this.doc.createRange();
14978         }
14979     },
14980     getParentElement: function()
14981     {
14982         
14983         this.assignDocWin();
14984         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14985         
14986         var range = this.createRange(sel);
14987          
14988         try {
14989             var p = range.commonAncestorContainer;
14990             while (p.nodeType == 3) { // text node
14991                 p = p.parentNode;
14992             }
14993             return p;
14994         } catch (e) {
14995             return null;
14996         }
14997     
14998     },
14999     /***
15000      *
15001      * Range intersection.. the hard stuff...
15002      *  '-1' = before
15003      *  '0' = hits..
15004      *  '1' = after.
15005      *         [ -- selected range --- ]
15006      *   [fail]                        [fail]
15007      *
15008      *    basically..
15009      *      if end is before start or  hits it. fail.
15010      *      if start is after end or hits it fail.
15011      *
15012      *   if either hits (but other is outside. - then it's not 
15013      *   
15014      *    
15015      **/
15016     
15017     
15018     // @see http://www.thismuchiknow.co.uk/?p=64.
15019     rangeIntersectsNode : function(range, node)
15020     {
15021         var nodeRange = node.ownerDocument.createRange();
15022         try {
15023             nodeRange.selectNode(node);
15024         } catch (e) {
15025             nodeRange.selectNodeContents(node);
15026         }
15027     
15028         var rangeStartRange = range.cloneRange();
15029         rangeStartRange.collapse(true);
15030     
15031         var rangeEndRange = range.cloneRange();
15032         rangeEndRange.collapse(false);
15033     
15034         var nodeStartRange = nodeRange.cloneRange();
15035         nodeStartRange.collapse(true);
15036     
15037         var nodeEndRange = nodeRange.cloneRange();
15038         nodeEndRange.collapse(false);
15039     
15040         return rangeStartRange.compareBoundaryPoints(
15041                  Range.START_TO_START, nodeEndRange) == -1 &&
15042                rangeEndRange.compareBoundaryPoints(
15043                  Range.START_TO_START, nodeStartRange) == 1;
15044         
15045          
15046     },
15047     rangeCompareNode : function(range, node)
15048     {
15049         var nodeRange = node.ownerDocument.createRange();
15050         try {
15051             nodeRange.selectNode(node);
15052         } catch (e) {
15053             nodeRange.selectNodeContents(node);
15054         }
15055         
15056         
15057         range.collapse(true);
15058     
15059         nodeRange.collapse(true);
15060      
15061         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15062         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15063          
15064         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15065         
15066         var nodeIsBefore   =  ss == 1;
15067         var nodeIsAfter    = ee == -1;
15068         
15069         if (nodeIsBefore && nodeIsAfter)
15070             return 0; // outer
15071         if (!nodeIsBefore && nodeIsAfter)
15072             return 1; //right trailed.
15073         
15074         if (nodeIsBefore && !nodeIsAfter)
15075             return 2;  // left trailed.
15076         // fully contined.
15077         return 3;
15078     },
15079
15080     // private? - in a new class?
15081     cleanUpPaste :  function()
15082     {
15083         // cleans up the whole document..
15084         Roo.log('cleanuppaste');
15085         
15086         this.cleanUpChildren(this.doc.body);
15087         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15088         if (clean != this.doc.body.innerHTML) {
15089             this.doc.body.innerHTML = clean;
15090         }
15091         
15092     },
15093     
15094     cleanWordChars : function(input) {// change the chars to hex code
15095         var he = Roo.HtmlEditorCore;
15096         
15097         var output = input;
15098         Roo.each(he.swapCodes, function(sw) { 
15099             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15100             
15101             output = output.replace(swapper, sw[1]);
15102         });
15103         
15104         return output;
15105     },
15106     
15107     
15108     cleanUpChildren : function (n)
15109     {
15110         if (!n.childNodes.length) {
15111             return;
15112         }
15113         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15114            this.cleanUpChild(n.childNodes[i]);
15115         }
15116     },
15117     
15118     
15119         
15120     
15121     cleanUpChild : function (node)
15122     {
15123         var ed = this;
15124         //console.log(node);
15125         if (node.nodeName == "#text") {
15126             // clean up silly Windows -- stuff?
15127             return; 
15128         }
15129         if (node.nodeName == "#comment") {
15130             node.parentNode.removeChild(node);
15131             // clean up silly Windows -- stuff?
15132             return; 
15133         }
15134         
15135         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15136             // remove node.
15137             node.parentNode.removeChild(node);
15138             return;
15139             
15140         }
15141         
15142         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15143         
15144         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15145         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15146         
15147         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15148         //    remove_keep_children = true;
15149         //}
15150         
15151         if (remove_keep_children) {
15152             this.cleanUpChildren(node);
15153             // inserts everything just before this node...
15154             while (node.childNodes.length) {
15155                 var cn = node.childNodes[0];
15156                 node.removeChild(cn);
15157                 node.parentNode.insertBefore(cn, node);
15158             }
15159             node.parentNode.removeChild(node);
15160             return;
15161         }
15162         
15163         if (!node.attributes || !node.attributes.length) {
15164             this.cleanUpChildren(node);
15165             return;
15166         }
15167         
15168         function cleanAttr(n,v)
15169         {
15170             
15171             if (v.match(/^\./) || v.match(/^\//)) {
15172                 return;
15173             }
15174             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15175                 return;
15176             }
15177             if (v.match(/^#/)) {
15178                 return;
15179             }
15180 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15181             node.removeAttribute(n);
15182             
15183         }
15184         
15185         function cleanStyle(n,v)
15186         {
15187             if (v.match(/expression/)) { //XSS?? should we even bother..
15188                 node.removeAttribute(n);
15189                 return;
15190             }
15191             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15192             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15193             
15194             
15195             var parts = v.split(/;/);
15196             var clean = [];
15197             
15198             Roo.each(parts, function(p) {
15199                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15200                 if (!p.length) {
15201                     return true;
15202                 }
15203                 var l = p.split(':').shift().replace(/\s+/g,'');
15204                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15205                 
15206                 if ( cblack.indexOf(l) > -1) {
15207 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15208                     //node.removeAttribute(n);
15209                     return true;
15210                 }
15211                 //Roo.log()
15212                 // only allow 'c whitelisted system attributes'
15213                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15214 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15215                     //node.removeAttribute(n);
15216                     return true;
15217                 }
15218                 
15219                 
15220                  
15221                 
15222                 clean.push(p);
15223                 return true;
15224             });
15225             if (clean.length) { 
15226                 node.setAttribute(n, clean.join(';'));
15227             } else {
15228                 node.removeAttribute(n);
15229             }
15230             
15231         }
15232         
15233         
15234         for (var i = node.attributes.length-1; i > -1 ; i--) {
15235             var a = node.attributes[i];
15236             //console.log(a);
15237             
15238             if (a.name.toLowerCase().substr(0,2)=='on')  {
15239                 node.removeAttribute(a.name);
15240                 continue;
15241             }
15242             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15243                 node.removeAttribute(a.name);
15244                 continue;
15245             }
15246             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15247                 cleanAttr(a.name,a.value); // fixme..
15248                 continue;
15249             }
15250             if (a.name == 'style') {
15251                 cleanStyle(a.name,a.value);
15252                 continue;
15253             }
15254             /// clean up MS crap..
15255             // tecnically this should be a list of valid class'es..
15256             
15257             
15258             if (a.name == 'class') {
15259                 if (a.value.match(/^Mso/)) {
15260                     node.className = '';
15261                 }
15262                 
15263                 if (a.value.match(/body/)) {
15264                     node.className = '';
15265                 }
15266                 continue;
15267             }
15268             
15269             // style cleanup!?
15270             // class cleanup?
15271             
15272         }
15273         
15274         
15275         this.cleanUpChildren(node);
15276         
15277         
15278     }
15279     
15280     
15281     // hide stuff that is not compatible
15282     /**
15283      * @event blur
15284      * @hide
15285      */
15286     /**
15287      * @event change
15288      * @hide
15289      */
15290     /**
15291      * @event focus
15292      * @hide
15293      */
15294     /**
15295      * @event specialkey
15296      * @hide
15297      */
15298     /**
15299      * @cfg {String} fieldClass @hide
15300      */
15301     /**
15302      * @cfg {String} focusClass @hide
15303      */
15304     /**
15305      * @cfg {String} autoCreate @hide
15306      */
15307     /**
15308      * @cfg {String} inputType @hide
15309      */
15310     /**
15311      * @cfg {String} invalidClass @hide
15312      */
15313     /**
15314      * @cfg {String} invalidText @hide
15315      */
15316     /**
15317      * @cfg {String} msgFx @hide
15318      */
15319     /**
15320      * @cfg {String} validateOnBlur @hide
15321      */
15322 });
15323
15324 Roo.HtmlEditorCore.white = [
15325         'area', 'br', 'img', 'input', 'hr', 'wbr',
15326         
15327        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15328        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15329        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15330        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15331        'table',   'ul',         'xmp', 
15332        
15333        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15334       'thead',   'tr', 
15335      
15336       'dir', 'menu', 'ol', 'ul', 'dl',
15337        
15338       'embed',  'object'
15339 ];
15340
15341
15342 Roo.HtmlEditorCore.black = [
15343     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15344         'applet', // 
15345         'base',   'basefont', 'bgsound', 'blink',  'body', 
15346         'frame',  'frameset', 'head',    'html',   'ilayer', 
15347         'iframe', 'layer',  'link',     'meta',    'object',   
15348         'script', 'style' ,'title',  'xml' // clean later..
15349 ];
15350 Roo.HtmlEditorCore.clean = [
15351     'script', 'style', 'title', 'xml'
15352 ];
15353 Roo.HtmlEditorCore.remove = [
15354     'font'
15355 ];
15356 // attributes..
15357
15358 Roo.HtmlEditorCore.ablack = [
15359     'on'
15360 ];
15361     
15362 Roo.HtmlEditorCore.aclean = [ 
15363     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15364 ];
15365
15366 // protocols..
15367 Roo.HtmlEditorCore.pwhite= [
15368         'http',  'https',  'mailto'
15369 ];
15370
15371 // white listed style attributes.
15372 Roo.HtmlEditorCore.cwhite= [
15373       //  'text-align', /// default is to allow most things..
15374       
15375          
15376 //        'font-size'//??
15377 ];
15378
15379 // black listed style attributes.
15380 Roo.HtmlEditorCore.cblack= [
15381       //  'font-size' -- this can be set by the project 
15382 ];
15383
15384
15385 Roo.HtmlEditorCore.swapCodes   =[ 
15386     [    8211, "--" ], 
15387     [    8212, "--" ], 
15388     [    8216,  "'" ],  
15389     [    8217, "'" ],  
15390     [    8220, '"' ],  
15391     [    8221, '"' ],  
15392     [    8226, "*" ],  
15393     [    8230, "..." ]
15394 ]; 
15395
15396     /*
15397  * - LGPL
15398  *
15399  * HtmlEditor
15400  * 
15401  */
15402
15403 /**
15404  * @class Roo.bootstrap.HtmlEditor
15405  * @extends Roo.bootstrap.TextArea
15406  * Bootstrap HtmlEditor class
15407
15408  * @constructor
15409  * Create a new HtmlEditor
15410  * @param {Object} config The config object
15411  */
15412
15413 Roo.bootstrap.HtmlEditor = function(config){
15414     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15415     if (!this.toolbars) {
15416         this.toolbars = [];
15417     }
15418     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15419     this.addEvents({
15420             /**
15421              * @event initialize
15422              * Fires when the editor is fully initialized (including the iframe)
15423              * @param {HtmlEditor} this
15424              */
15425             initialize: true,
15426             /**
15427              * @event activate
15428              * Fires when the editor is first receives the focus. Any insertion must wait
15429              * until after this event.
15430              * @param {HtmlEditor} this
15431              */
15432             activate: true,
15433              /**
15434              * @event beforesync
15435              * Fires before the textarea is updated with content from the editor iframe. Return false
15436              * to cancel the sync.
15437              * @param {HtmlEditor} this
15438              * @param {String} html
15439              */
15440             beforesync: true,
15441              /**
15442              * @event beforepush
15443              * Fires before the iframe editor is updated with content from the textarea. Return false
15444              * to cancel the push.
15445              * @param {HtmlEditor} this
15446              * @param {String} html
15447              */
15448             beforepush: true,
15449              /**
15450              * @event sync
15451              * Fires when the textarea is updated with content from the editor iframe.
15452              * @param {HtmlEditor} this
15453              * @param {String} html
15454              */
15455             sync: true,
15456              /**
15457              * @event push
15458              * Fires when the iframe editor is updated with content from the textarea.
15459              * @param {HtmlEditor} this
15460              * @param {String} html
15461              */
15462             push: true,
15463              /**
15464              * @event editmodechange
15465              * Fires when the editor switches edit modes
15466              * @param {HtmlEditor} this
15467              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15468              */
15469             editmodechange: true,
15470             /**
15471              * @event editorevent
15472              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15473              * @param {HtmlEditor} this
15474              */
15475             editorevent: true,
15476             /**
15477              * @event firstfocus
15478              * Fires when on first focus - needed by toolbars..
15479              * @param {HtmlEditor} this
15480              */
15481             firstfocus: true,
15482             /**
15483              * @event autosave
15484              * Auto save the htmlEditor value as a file into Events
15485              * @param {HtmlEditor} this
15486              */
15487             autosave: true,
15488             /**
15489              * @event savedpreview
15490              * preview the saved version of htmlEditor
15491              * @param {HtmlEditor} this
15492              */
15493             savedpreview: true
15494         });
15495 };
15496
15497
15498 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15499     
15500     
15501       /**
15502      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15503      */
15504     toolbars : false,
15505    
15506      /**
15507      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15508      *                        Roo.resizable.
15509      */
15510     resizable : false,
15511      /**
15512      * @cfg {Number} height (in pixels)
15513      */   
15514     height: 300,
15515    /**
15516      * @cfg {Number} width (in pixels)
15517      */   
15518     width: false,
15519     
15520     /**
15521      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15522      * 
15523      */
15524     stylesheets: false,
15525     
15526     // id of frame..
15527     frameId: false,
15528     
15529     // private properties
15530     validationEvent : false,
15531     deferHeight: true,
15532     initialized : false,
15533     activated : false,
15534     
15535     onFocus : Roo.emptyFn,
15536     iframePad:3,
15537     hideMode:'offsets',
15538     
15539     
15540     tbContainer : false,
15541     
15542     toolbarContainer :function() {
15543         return this.wrap.select('.x-html-editor-tb',true).first();
15544     },
15545
15546     /**
15547      * Protected method that will not generally be called directly. It
15548      * is called when the editor creates its toolbar. Override this method if you need to
15549      * add custom toolbar buttons.
15550      * @param {HtmlEditor} editor
15551      */
15552     createToolbar : function(){
15553         
15554         Roo.log("create toolbars");
15555         
15556         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15557         this.toolbars[0].render(this.toolbarContainer());
15558         
15559         return;
15560         
15561 //        if (!editor.toolbars || !editor.toolbars.length) {
15562 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15563 //        }
15564 //        
15565 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15566 //            editor.toolbars[i] = Roo.factory(
15567 //                    typeof(editor.toolbars[i]) == 'string' ?
15568 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15569 //                Roo.bootstrap.HtmlEditor);
15570 //            editor.toolbars[i].init(editor);
15571 //        }
15572     },
15573
15574      
15575     // private
15576     onRender : function(ct, position)
15577     {
15578        // Roo.log("Call onRender: " + this.xtype);
15579         var _t = this;
15580         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15581       
15582         this.wrap = this.inputEl().wrap({
15583             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15584         });
15585         
15586         this.editorcore.onRender(ct, position);
15587          
15588         if (this.resizable) {
15589             this.resizeEl = new Roo.Resizable(this.wrap, {
15590                 pinned : true,
15591                 wrap: true,
15592                 dynamic : true,
15593                 minHeight : this.height,
15594                 height: this.height,
15595                 handles : this.resizable,
15596                 width: this.width,
15597                 listeners : {
15598                     resize : function(r, w, h) {
15599                         _t.onResize(w,h); // -something
15600                     }
15601                 }
15602             });
15603             
15604         }
15605         this.createToolbar(this);
15606        
15607         
15608         if(!this.width && this.resizable){
15609             this.setSize(this.wrap.getSize());
15610         }
15611         if (this.resizeEl) {
15612             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15613             // should trigger onReize..
15614         }
15615         
15616     },
15617
15618     // private
15619     onResize : function(w, h)
15620     {
15621         Roo.log('resize: ' +w + ',' + h );
15622         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15623         var ew = false;
15624         var eh = false;
15625         
15626         if(this.inputEl() ){
15627             if(typeof w == 'number'){
15628                 var aw = w - this.wrap.getFrameWidth('lr');
15629                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15630                 ew = aw;
15631             }
15632             if(typeof h == 'number'){
15633                  var tbh = -11;  // fixme it needs to tool bar size!
15634                 for (var i =0; i < this.toolbars.length;i++) {
15635                     // fixme - ask toolbars for heights?
15636                     tbh += this.toolbars[i].el.getHeight();
15637                     //if (this.toolbars[i].footer) {
15638                     //    tbh += this.toolbars[i].footer.el.getHeight();
15639                     //}
15640                 }
15641               
15642                 
15643                 
15644                 
15645                 
15646                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15647                 ah -= 5; // knock a few pixes off for look..
15648                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15649                 var eh = ah;
15650             }
15651         }
15652         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15653         this.editorcore.onResize(ew,eh);
15654         
15655     },
15656
15657     /**
15658      * Toggles the editor between standard and source edit mode.
15659      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15660      */
15661     toggleSourceEdit : function(sourceEditMode)
15662     {
15663         this.editorcore.toggleSourceEdit(sourceEditMode);
15664         
15665         if(this.editorcore.sourceEditMode){
15666             Roo.log('editor - showing textarea');
15667             
15668 //            Roo.log('in');
15669 //            Roo.log(this.syncValue());
15670             this.syncValue();
15671             this.inputEl().removeClass('hide');
15672             this.inputEl().dom.removeAttribute('tabIndex');
15673             this.inputEl().focus();
15674         }else{
15675             Roo.log('editor - hiding textarea');
15676 //            Roo.log('out')
15677 //            Roo.log(this.pushValue()); 
15678             this.pushValue();
15679             
15680             this.inputEl().addClass('hide');
15681             this.inputEl().dom.setAttribute('tabIndex', -1);
15682             //this.deferFocus();
15683         }
15684          
15685         if(this.resizable){
15686             this.setSize(this.wrap.getSize());
15687         }
15688         
15689         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15690     },
15691  
15692     // private (for BoxComponent)
15693     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15694
15695     // private (for BoxComponent)
15696     getResizeEl : function(){
15697         return this.wrap;
15698     },
15699
15700     // private (for BoxComponent)
15701     getPositionEl : function(){
15702         return this.wrap;
15703     },
15704
15705     // private
15706     initEvents : function(){
15707         this.originalValue = this.getValue();
15708     },
15709
15710 //    /**
15711 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15712 //     * @method
15713 //     */
15714 //    markInvalid : Roo.emptyFn,
15715 //    /**
15716 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15717 //     * @method
15718 //     */
15719 //    clearInvalid : Roo.emptyFn,
15720
15721     setValue : function(v){
15722         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15723         this.editorcore.pushValue();
15724     },
15725
15726      
15727     // private
15728     deferFocus : function(){
15729         this.focus.defer(10, this);
15730     },
15731
15732     // doc'ed in Field
15733     focus : function(){
15734         this.editorcore.focus();
15735         
15736     },
15737       
15738
15739     // private
15740     onDestroy : function(){
15741         
15742         
15743         
15744         if(this.rendered){
15745             
15746             for (var i =0; i < this.toolbars.length;i++) {
15747                 // fixme - ask toolbars for heights?
15748                 this.toolbars[i].onDestroy();
15749             }
15750             
15751             this.wrap.dom.innerHTML = '';
15752             this.wrap.remove();
15753         }
15754     },
15755
15756     // private
15757     onFirstFocus : function(){
15758         //Roo.log("onFirstFocus");
15759         this.editorcore.onFirstFocus();
15760          for (var i =0; i < this.toolbars.length;i++) {
15761             this.toolbars[i].onFirstFocus();
15762         }
15763         
15764     },
15765     
15766     // private
15767     syncValue : function()
15768     {   
15769         this.editorcore.syncValue();
15770     },
15771     
15772     pushValue : function()
15773     {   
15774         this.editorcore.pushValue();
15775     }
15776      
15777     
15778     // hide stuff that is not compatible
15779     /**
15780      * @event blur
15781      * @hide
15782      */
15783     /**
15784      * @event change
15785      * @hide
15786      */
15787     /**
15788      * @event focus
15789      * @hide
15790      */
15791     /**
15792      * @event specialkey
15793      * @hide
15794      */
15795     /**
15796      * @cfg {String} fieldClass @hide
15797      */
15798     /**
15799      * @cfg {String} focusClass @hide
15800      */
15801     /**
15802      * @cfg {String} autoCreate @hide
15803      */
15804     /**
15805      * @cfg {String} inputType @hide
15806      */
15807     /**
15808      * @cfg {String} invalidClass @hide
15809      */
15810     /**
15811      * @cfg {String} invalidText @hide
15812      */
15813     /**
15814      * @cfg {String} msgFx @hide
15815      */
15816     /**
15817      * @cfg {String} validateOnBlur @hide
15818      */
15819 });
15820  
15821     
15822    
15823    
15824    
15825       
15826
15827 /**
15828  * @class Roo.bootstrap.HtmlEditorToolbar1
15829  * Basic Toolbar
15830  * 
15831  * Usage:
15832  *
15833  new Roo.bootstrap.HtmlEditor({
15834     ....
15835     toolbars : [
15836         new Roo.bootstrap.HtmlEditorToolbar1({
15837             disable : { fonts: 1 , format: 1, ..., ... , ...],
15838             btns : [ .... ]
15839         })
15840     }
15841      
15842  * 
15843  * @cfg {Object} disable List of elements to disable..
15844  * @cfg {Array} btns List of additional buttons.
15845  * 
15846  * 
15847  * NEEDS Extra CSS? 
15848  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15849  */
15850  
15851 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15852 {
15853     
15854     Roo.apply(this, config);
15855     
15856     // default disabled, based on 'good practice'..
15857     this.disable = this.disable || {};
15858     Roo.applyIf(this.disable, {
15859         fontSize : true,
15860         colors : true,
15861         specialElements : true
15862     });
15863     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15864     
15865     this.editor = config.editor;
15866     this.editorcore = config.editor.editorcore;
15867     
15868     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15869     
15870     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15871     // dont call parent... till later.
15872 }
15873 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
15874     
15875     
15876     bar : true,
15877     
15878     editor : false,
15879     editorcore : false,
15880     
15881     
15882     formats : [
15883         "p" ,  
15884         "h1","h2","h3","h4","h5","h6", 
15885         "pre", "code", 
15886         "abbr", "acronym", "address", "cite", "samp", "var",
15887         'div','span'
15888     ],
15889     
15890     onRender : function(ct, position)
15891     {
15892        // Roo.log("Call onRender: " + this.xtype);
15893         
15894        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15895        Roo.log(this.el);
15896        this.el.dom.style.marginBottom = '0';
15897        var _this = this;
15898        var editorcore = this.editorcore;
15899        var editor= this.editor;
15900        
15901        var children = [];
15902        var btn = function(id,cmd , toggle, handler){
15903        
15904             var  event = toggle ? 'toggle' : 'click';
15905        
15906             var a = {
15907                 size : 'sm',
15908                 xtype: 'Button',
15909                 xns: Roo.bootstrap,
15910                 glyphicon : id,
15911                 cmd : id || cmd,
15912                 enableToggle:toggle !== false,
15913                 //html : 'submit'
15914                 pressed : toggle ? false : null,
15915                 listeners : {}
15916             }
15917             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15918                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15919             }
15920             children.push(a);
15921             return a;
15922        }
15923         
15924         var style = {
15925                 xtype: 'Button',
15926                 size : 'sm',
15927                 xns: Roo.bootstrap,
15928                 glyphicon : 'font',
15929                 //html : 'submit'
15930                 menu : {
15931                     xtype: 'Menu',
15932                     xns: Roo.bootstrap,
15933                     items:  []
15934                 }
15935         };
15936         Roo.each(this.formats, function(f) {
15937             style.menu.items.push({
15938                 xtype :'MenuItem',
15939                 xns: Roo.bootstrap,
15940                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15941                 tagname : f,
15942                 listeners : {
15943                     click : function()
15944                     {
15945                         editorcore.insertTag(this.tagname);
15946                         editor.focus();
15947                     }
15948                 }
15949                 
15950             });
15951         });
15952          children.push(style);   
15953             
15954             
15955         btn('bold',false,true);
15956         btn('italic',false,true);
15957         btn('align-left', 'justifyleft',true);
15958         btn('align-center', 'justifycenter',true);
15959         btn('align-right' , 'justifyright',true);
15960         btn('link', false, false, function(btn) {
15961             //Roo.log("create link?");
15962             var url = prompt(this.createLinkText, this.defaultLinkValue);
15963             if(url && url != 'http:/'+'/'){
15964                 this.editorcore.relayCmd('createlink', url);
15965             }
15966         }),
15967         btn('list','insertunorderedlist',true);
15968         btn('pencil', false,true, function(btn){
15969                 Roo.log(this);
15970                 
15971                 this.toggleSourceEdit(btn.pressed);
15972         });
15973         /*
15974         var cog = {
15975                 xtype: 'Button',
15976                 size : 'sm',
15977                 xns: Roo.bootstrap,
15978                 glyphicon : 'cog',
15979                 //html : 'submit'
15980                 menu : {
15981                     xtype: 'Menu',
15982                     xns: Roo.bootstrap,
15983                     items:  []
15984                 }
15985         };
15986         
15987         cog.menu.items.push({
15988             xtype :'MenuItem',
15989             xns: Roo.bootstrap,
15990             html : Clean styles,
15991             tagname : f,
15992             listeners : {
15993                 click : function()
15994                 {
15995                     editorcore.insertTag(this.tagname);
15996                     editor.focus();
15997                 }
15998             }
15999             
16000         });
16001        */
16002         
16003          
16004        this.xtype = 'NavSimplebar';
16005         
16006         for(var i=0;i< children.length;i++) {
16007             
16008             this.buttons.add(this.addxtypeChild(children[i]));
16009             
16010         }
16011         
16012         editor.on('editorevent', this.updateToolbar, this);
16013     },
16014     onBtnClick : function(id)
16015     {
16016        this.editorcore.relayCmd(id);
16017        this.editorcore.focus();
16018     },
16019     
16020     /**
16021      * Protected method that will not generally be called directly. It triggers
16022      * a toolbar update by reading the markup state of the current selection in the editor.
16023      */
16024     updateToolbar: function(){
16025
16026         if(!this.editorcore.activated){
16027             this.editor.onFirstFocus(); // is this neeed?
16028             return;
16029         }
16030
16031         var btns = this.buttons; 
16032         var doc = this.editorcore.doc;
16033         btns.get('bold').setActive(doc.queryCommandState('bold'));
16034         btns.get('italic').setActive(doc.queryCommandState('italic'));
16035         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16036         
16037         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16038         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16039         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16040         
16041         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16042         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16043          /*
16044         
16045         var ans = this.editorcore.getAllAncestors();
16046         if (this.formatCombo) {
16047             
16048             
16049             var store = this.formatCombo.store;
16050             this.formatCombo.setValue("");
16051             for (var i =0; i < ans.length;i++) {
16052                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16053                     // select it..
16054                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16055                     break;
16056                 }
16057             }
16058         }
16059         
16060         
16061         
16062         // hides menus... - so this cant be on a menu...
16063         Roo.bootstrap.MenuMgr.hideAll();
16064         */
16065         Roo.bootstrap.MenuMgr.hideAll();
16066         //this.editorsyncValue();
16067     },
16068     onFirstFocus: function() {
16069         this.buttons.each(function(item){
16070            item.enable();
16071         });
16072     },
16073     toggleSourceEdit : function(sourceEditMode){
16074         
16075           
16076         if(sourceEditMode){
16077             Roo.log("disabling buttons");
16078            this.buttons.each( function(item){
16079                 if(item.cmd != 'pencil'){
16080                     item.disable();
16081                 }
16082             });
16083           
16084         }else{
16085             Roo.log("enabling buttons");
16086             if(this.editorcore.initialized){
16087                 this.buttons.each( function(item){
16088                     item.enable();
16089                 });
16090             }
16091             
16092         }
16093         Roo.log("calling toggole on editor");
16094         // tell the editor that it's been pressed..
16095         this.editor.toggleSourceEdit(sourceEditMode);
16096        
16097     }
16098 });
16099
16100
16101
16102
16103
16104 /**
16105  * @class Roo.bootstrap.Table.AbstractSelectionModel
16106  * @extends Roo.util.Observable
16107  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16108  * implemented by descendant classes.  This class should not be directly instantiated.
16109  * @constructor
16110  */
16111 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16112     this.locked = false;
16113     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16114 };
16115
16116
16117 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16118     /** @ignore Called by the grid automatically. Do not call directly. */
16119     init : function(grid){
16120         this.grid = grid;
16121         this.initEvents();
16122     },
16123
16124     /**
16125      * Locks the selections.
16126      */
16127     lock : function(){
16128         this.locked = true;
16129     },
16130
16131     /**
16132      * Unlocks the selections.
16133      */
16134     unlock : function(){
16135         this.locked = false;
16136     },
16137
16138     /**
16139      * Returns true if the selections are locked.
16140      * @return {Boolean}
16141      */
16142     isLocked : function(){
16143         return this.locked;
16144     }
16145 });
16146 /**
16147  * @class Roo.bootstrap.Table.ColumnModel
16148  * @extends Roo.util.Observable
16149  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16150  * the columns in the table.
16151  
16152  * @constructor
16153  * @param {Object} config An Array of column config objects. See this class's
16154  * config objects for details.
16155 */
16156 Roo.bootstrap.Table.ColumnModel = function(config){
16157         /**
16158      * The config passed into the constructor
16159      */
16160     this.config = config;
16161     this.lookup = {};
16162
16163     // if no id, create one
16164     // if the column does not have a dataIndex mapping,
16165     // map it to the order it is in the config
16166     for(var i = 0, len = config.length; i < len; i++){
16167         var c = config[i];
16168         if(typeof c.dataIndex == "undefined"){
16169             c.dataIndex = i;
16170         }
16171         if(typeof c.renderer == "string"){
16172             c.renderer = Roo.util.Format[c.renderer];
16173         }
16174         if(typeof c.id == "undefined"){
16175             c.id = Roo.id();
16176         }
16177 //        if(c.editor && c.editor.xtype){
16178 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16179 //        }
16180 //        if(c.editor && c.editor.isFormField){
16181 //            c.editor = new Roo.grid.GridEditor(c.editor);
16182 //        }
16183
16184         this.lookup[c.id] = c;
16185     }
16186
16187     /**
16188      * The width of columns which have no width specified (defaults to 100)
16189      * @type Number
16190      */
16191     this.defaultWidth = 100;
16192
16193     /**
16194      * Default sortable of columns which have no sortable specified (defaults to false)
16195      * @type Boolean
16196      */
16197     this.defaultSortable = false;
16198
16199     this.addEvents({
16200         /**
16201              * @event widthchange
16202              * Fires when the width of a column changes.
16203              * @param {ColumnModel} this
16204              * @param {Number} columnIndex The column index
16205              * @param {Number} newWidth The new width
16206              */
16207             "widthchange": true,
16208         /**
16209              * @event headerchange
16210              * Fires when the text of a header changes.
16211              * @param {ColumnModel} this
16212              * @param {Number} columnIndex The column index
16213              * @param {Number} newText The new header text
16214              */
16215             "headerchange": true,
16216         /**
16217              * @event hiddenchange
16218              * Fires when a column is hidden or "unhidden".
16219              * @param {ColumnModel} this
16220              * @param {Number} columnIndex The column index
16221              * @param {Boolean} hidden true if hidden, false otherwise
16222              */
16223             "hiddenchange": true,
16224             /**
16225          * @event columnmoved
16226          * Fires when a column is moved.
16227          * @param {ColumnModel} this
16228          * @param {Number} oldIndex
16229          * @param {Number} newIndex
16230          */
16231         "columnmoved" : true,
16232         /**
16233          * @event columlockchange
16234          * Fires when a column's locked state is changed
16235          * @param {ColumnModel} this
16236          * @param {Number} colIndex
16237          * @param {Boolean} locked true if locked
16238          */
16239         "columnlockchange" : true
16240     });
16241     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16242 };
16243 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16244     /**
16245      * @cfg {String} header The header text to display in the Grid view.
16246      */
16247     /**
16248      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16249      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16250      * specified, the column's index is used as an index into the Record's data Array.
16251      */
16252     /**
16253      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16254      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16255      */
16256     /**
16257      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16258      * Defaults to the value of the {@link #defaultSortable} property.
16259      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16260      */
16261     /**
16262      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16263      */
16264     /**
16265      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16266      */
16267     /**
16268      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16269      */
16270     /**
16271      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16272      */
16273     /**
16274      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16275      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16276      * default renderer uses the raw data value.
16277      */
16278     /**
16279      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16280      */
16281
16282     /**
16283      * Returns the id of the column at the specified index.
16284      * @param {Number} index The column index
16285      * @return {String} the id
16286      */
16287     getColumnId : function(index){
16288         return this.config[index].id;
16289     },
16290
16291     /**
16292      * Returns the column for a specified id.
16293      * @param {String} id The column id
16294      * @return {Object} the column
16295      */
16296     getColumnById : function(id){
16297         return this.lookup[id];
16298     },
16299
16300     
16301     /**
16302      * Returns the column for a specified dataIndex.
16303      * @param {String} dataIndex The column dataIndex
16304      * @return {Object|Boolean} the column or false if not found
16305      */
16306     getColumnByDataIndex: function(dataIndex){
16307         var index = this.findColumnIndex(dataIndex);
16308         return index > -1 ? this.config[index] : false;
16309     },
16310     
16311     /**
16312      * Returns the index for a specified column id.
16313      * @param {String} id The column id
16314      * @return {Number} the index, or -1 if not found
16315      */
16316     getIndexById : function(id){
16317         for(var i = 0, len = this.config.length; i < len; i++){
16318             if(this.config[i].id == id){
16319                 return i;
16320             }
16321         }
16322         return -1;
16323     },
16324     
16325     /**
16326      * Returns the index for a specified column dataIndex.
16327      * @param {String} dataIndex The column dataIndex
16328      * @return {Number} the index, or -1 if not found
16329      */
16330     
16331     findColumnIndex : function(dataIndex){
16332         for(var i = 0, len = this.config.length; i < len; i++){
16333             if(this.config[i].dataIndex == dataIndex){
16334                 return i;
16335             }
16336         }
16337         return -1;
16338     },
16339     
16340     
16341     moveColumn : function(oldIndex, newIndex){
16342         var c = this.config[oldIndex];
16343         this.config.splice(oldIndex, 1);
16344         this.config.splice(newIndex, 0, c);
16345         this.dataMap = null;
16346         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16347     },
16348
16349     isLocked : function(colIndex){
16350         return this.config[colIndex].locked === true;
16351     },
16352
16353     setLocked : function(colIndex, value, suppressEvent){
16354         if(this.isLocked(colIndex) == value){
16355             return;
16356         }
16357         this.config[colIndex].locked = value;
16358         if(!suppressEvent){
16359             this.fireEvent("columnlockchange", this, colIndex, value);
16360         }
16361     },
16362
16363     getTotalLockedWidth : function(){
16364         var totalWidth = 0;
16365         for(var i = 0; i < this.config.length; i++){
16366             if(this.isLocked(i) && !this.isHidden(i)){
16367                 this.totalWidth += this.getColumnWidth(i);
16368             }
16369         }
16370         return totalWidth;
16371     },
16372
16373     getLockedCount : function(){
16374         for(var i = 0, len = this.config.length; i < len; i++){
16375             if(!this.isLocked(i)){
16376                 return i;
16377             }
16378         }
16379     },
16380
16381     /**
16382      * Returns the number of columns.
16383      * @return {Number}
16384      */
16385     getColumnCount : function(visibleOnly){
16386         if(visibleOnly === true){
16387             var c = 0;
16388             for(var i = 0, len = this.config.length; i < len; i++){
16389                 if(!this.isHidden(i)){
16390                     c++;
16391                 }
16392             }
16393             return c;
16394         }
16395         return this.config.length;
16396     },
16397
16398     /**
16399      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16400      * @param {Function} fn
16401      * @param {Object} scope (optional)
16402      * @return {Array} result
16403      */
16404     getColumnsBy : function(fn, scope){
16405         var r = [];
16406         for(var i = 0, len = this.config.length; i < len; i++){
16407             var c = this.config[i];
16408             if(fn.call(scope||this, c, i) === true){
16409                 r[r.length] = c;
16410             }
16411         }
16412         return r;
16413     },
16414
16415     /**
16416      * Returns true if the specified column is sortable.
16417      * @param {Number} col The column index
16418      * @return {Boolean}
16419      */
16420     isSortable : function(col){
16421         if(typeof this.config[col].sortable == "undefined"){
16422             return this.defaultSortable;
16423         }
16424         return this.config[col].sortable;
16425     },
16426
16427     /**
16428      * Returns the rendering (formatting) function defined for the column.
16429      * @param {Number} col The column index.
16430      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16431      */
16432     getRenderer : function(col){
16433         if(!this.config[col].renderer){
16434             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16435         }
16436         return this.config[col].renderer;
16437     },
16438
16439     /**
16440      * Sets the rendering (formatting) function for a column.
16441      * @param {Number} col The column index
16442      * @param {Function} fn The function to use to process the cell's raw data
16443      * to return HTML markup for the grid view. The render function is called with
16444      * the following parameters:<ul>
16445      * <li>Data value.</li>
16446      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16447      * <li>css A CSS style string to apply to the table cell.</li>
16448      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16449      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16450      * <li>Row index</li>
16451      * <li>Column index</li>
16452      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16453      */
16454     setRenderer : function(col, fn){
16455         this.config[col].renderer = fn;
16456     },
16457
16458     /**
16459      * Returns the width for the specified column.
16460      * @param {Number} col The column index
16461      * @return {Number}
16462      */
16463     getColumnWidth : function(col){
16464         return this.config[col].width * 1 || this.defaultWidth;
16465     },
16466
16467     /**
16468      * Sets the width for a column.
16469      * @param {Number} col The column index
16470      * @param {Number} width The new width
16471      */
16472     setColumnWidth : function(col, width, suppressEvent){
16473         this.config[col].width = width;
16474         this.totalWidth = null;
16475         if(!suppressEvent){
16476              this.fireEvent("widthchange", this, col, width);
16477         }
16478     },
16479
16480     /**
16481      * Returns the total width of all columns.
16482      * @param {Boolean} includeHidden True to include hidden column widths
16483      * @return {Number}
16484      */
16485     getTotalWidth : function(includeHidden){
16486         if(!this.totalWidth){
16487             this.totalWidth = 0;
16488             for(var i = 0, len = this.config.length; i < len; i++){
16489                 if(includeHidden || !this.isHidden(i)){
16490                     this.totalWidth += this.getColumnWidth(i);
16491                 }
16492             }
16493         }
16494         return this.totalWidth;
16495     },
16496
16497     /**
16498      * Returns the header for the specified column.
16499      * @param {Number} col The column index
16500      * @return {String}
16501      */
16502     getColumnHeader : function(col){
16503         return this.config[col].header;
16504     },
16505
16506     /**
16507      * Sets the header for a column.
16508      * @param {Number} col The column index
16509      * @param {String} header The new header
16510      */
16511     setColumnHeader : function(col, header){
16512         this.config[col].header = header;
16513         this.fireEvent("headerchange", this, col, header);
16514     },
16515
16516     /**
16517      * Returns the tooltip for the specified column.
16518      * @param {Number} col The column index
16519      * @return {String}
16520      */
16521     getColumnTooltip : function(col){
16522             return this.config[col].tooltip;
16523     },
16524     /**
16525      * Sets the tooltip for a column.
16526      * @param {Number} col The column index
16527      * @param {String} tooltip The new tooltip
16528      */
16529     setColumnTooltip : function(col, tooltip){
16530             this.config[col].tooltip = tooltip;
16531     },
16532
16533     /**
16534      * Returns the dataIndex for the specified column.
16535      * @param {Number} col The column index
16536      * @return {Number}
16537      */
16538     getDataIndex : function(col){
16539         return this.config[col].dataIndex;
16540     },
16541
16542     /**
16543      * Sets the dataIndex for a column.
16544      * @param {Number} col The column index
16545      * @param {Number} dataIndex The new dataIndex
16546      */
16547     setDataIndex : function(col, dataIndex){
16548         this.config[col].dataIndex = dataIndex;
16549     },
16550
16551     
16552     
16553     /**
16554      * Returns true if the cell is editable.
16555      * @param {Number} colIndex The column index
16556      * @param {Number} rowIndex The row index
16557      * @return {Boolean}
16558      */
16559     isCellEditable : function(colIndex, rowIndex){
16560         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16561     },
16562
16563     /**
16564      * Returns the editor defined for the cell/column.
16565      * return false or null to disable editing.
16566      * @param {Number} colIndex The column index
16567      * @param {Number} rowIndex The row index
16568      * @return {Object}
16569      */
16570     getCellEditor : function(colIndex, rowIndex){
16571         return this.config[colIndex].editor;
16572     },
16573
16574     /**
16575      * Sets if a column is editable.
16576      * @param {Number} col The column index
16577      * @param {Boolean} editable True if the column is editable
16578      */
16579     setEditable : function(col, editable){
16580         this.config[col].editable = editable;
16581     },
16582
16583
16584     /**
16585      * Returns true if the column is hidden.
16586      * @param {Number} colIndex The column index
16587      * @return {Boolean}
16588      */
16589     isHidden : function(colIndex){
16590         return this.config[colIndex].hidden;
16591     },
16592
16593
16594     /**
16595      * Returns true if the column width cannot be changed
16596      */
16597     isFixed : function(colIndex){
16598         return this.config[colIndex].fixed;
16599     },
16600
16601     /**
16602      * Returns true if the column can be resized
16603      * @return {Boolean}
16604      */
16605     isResizable : function(colIndex){
16606         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16607     },
16608     /**
16609      * Sets if a column is hidden.
16610      * @param {Number} colIndex The column index
16611      * @param {Boolean} hidden True if the column is hidden
16612      */
16613     setHidden : function(colIndex, hidden){
16614         this.config[colIndex].hidden = hidden;
16615         this.totalWidth = null;
16616         this.fireEvent("hiddenchange", this, colIndex, hidden);
16617     },
16618
16619     /**
16620      * Sets the editor for a column.
16621      * @param {Number} col The column index
16622      * @param {Object} editor The editor object
16623      */
16624     setEditor : function(col, editor){
16625         this.config[col].editor = editor;
16626     }
16627 });
16628
16629 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16630         if(typeof value == "string" && value.length < 1){
16631             return "&#160;";
16632         }
16633         return value;
16634 };
16635
16636 // Alias for backwards compatibility
16637 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16638
16639 /**
16640  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16641  * @class Roo.bootstrap.Table.RowSelectionModel
16642  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16643  * It supports multiple selections and keyboard selection/navigation. 
16644  * @constructor
16645  * @param {Object} config
16646  */
16647
16648 Roo.bootstrap.Table.RowSelectionModel = function(config){
16649     Roo.apply(this, config);
16650     this.selections = new Roo.util.MixedCollection(false, function(o){
16651         return o.id;
16652     });
16653
16654     this.last = false;
16655     this.lastActive = false;
16656
16657     this.addEvents({
16658         /**
16659              * @event selectionchange
16660              * Fires when the selection changes
16661              * @param {SelectionModel} this
16662              */
16663             "selectionchange" : true,
16664         /**
16665              * @event afterselectionchange
16666              * Fires after the selection changes (eg. by key press or clicking)
16667              * @param {SelectionModel} this
16668              */
16669             "afterselectionchange" : true,
16670         /**
16671              * @event beforerowselect
16672              * Fires when a row is selected being selected, return false to cancel.
16673              * @param {SelectionModel} this
16674              * @param {Number} rowIndex The selected index
16675              * @param {Boolean} keepExisting False if other selections will be cleared
16676              */
16677             "beforerowselect" : true,
16678         /**
16679              * @event rowselect
16680              * Fires when a row is selected.
16681              * @param {SelectionModel} this
16682              * @param {Number} rowIndex The selected index
16683              * @param {Roo.data.Record} r The record
16684              */
16685             "rowselect" : true,
16686         /**
16687              * @event rowdeselect
16688              * Fires when a row is deselected.
16689              * @param {SelectionModel} this
16690              * @param {Number} rowIndex The selected index
16691              */
16692         "rowdeselect" : true
16693     });
16694     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16695     this.locked = false;
16696 };
16697
16698 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16699     /**
16700      * @cfg {Boolean} singleSelect
16701      * True to allow selection of only one row at a time (defaults to false)
16702      */
16703     singleSelect : false,
16704
16705     // private
16706     initEvents : function(){
16707
16708         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16709             this.grid.on("mousedown", this.handleMouseDown, this);
16710         }else{ // allow click to work like normal
16711             this.grid.on("rowclick", this.handleDragableRowClick, this);
16712         }
16713
16714         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16715             "up" : function(e){
16716                 if(!e.shiftKey){
16717                     this.selectPrevious(e.shiftKey);
16718                 }else if(this.last !== false && this.lastActive !== false){
16719                     var last = this.last;
16720                     this.selectRange(this.last,  this.lastActive-1);
16721                     this.grid.getView().focusRow(this.lastActive);
16722                     if(last !== false){
16723                         this.last = last;
16724                     }
16725                 }else{
16726                     this.selectFirstRow();
16727                 }
16728                 this.fireEvent("afterselectionchange", this);
16729             },
16730             "down" : function(e){
16731                 if(!e.shiftKey){
16732                     this.selectNext(e.shiftKey);
16733                 }else if(this.last !== false && this.lastActive !== false){
16734                     var last = this.last;
16735                     this.selectRange(this.last,  this.lastActive+1);
16736                     this.grid.getView().focusRow(this.lastActive);
16737                     if(last !== false){
16738                         this.last = last;
16739                     }
16740                 }else{
16741                     this.selectFirstRow();
16742                 }
16743                 this.fireEvent("afterselectionchange", this);
16744             },
16745             scope: this
16746         });
16747
16748         var view = this.grid.view;
16749         view.on("refresh", this.onRefresh, this);
16750         view.on("rowupdated", this.onRowUpdated, this);
16751         view.on("rowremoved", this.onRemove, this);
16752     },
16753
16754     // private
16755     onRefresh : function(){
16756         var ds = this.grid.dataSource, i, v = this.grid.view;
16757         var s = this.selections;
16758         s.each(function(r){
16759             if((i = ds.indexOfId(r.id)) != -1){
16760                 v.onRowSelect(i);
16761             }else{
16762                 s.remove(r);
16763             }
16764         });
16765     },
16766
16767     // private
16768     onRemove : function(v, index, r){
16769         this.selections.remove(r);
16770     },
16771
16772     // private
16773     onRowUpdated : function(v, index, r){
16774         if(this.isSelected(r)){
16775             v.onRowSelect(index);
16776         }
16777     },
16778
16779     /**
16780      * Select records.
16781      * @param {Array} records The records to select
16782      * @param {Boolean} keepExisting (optional) True to keep existing selections
16783      */
16784     selectRecords : function(records, keepExisting){
16785         if(!keepExisting){
16786             this.clearSelections();
16787         }
16788         var ds = this.grid.dataSource;
16789         for(var i = 0, len = records.length; i < len; i++){
16790             this.selectRow(ds.indexOf(records[i]), true);
16791         }
16792     },
16793
16794     /**
16795      * Gets the number of selected rows.
16796      * @return {Number}
16797      */
16798     getCount : function(){
16799         return this.selections.length;
16800     },
16801
16802     /**
16803      * Selects the first row in the grid.
16804      */
16805     selectFirstRow : function(){
16806         this.selectRow(0);
16807     },
16808
16809     /**
16810      * Select the last row.
16811      * @param {Boolean} keepExisting (optional) True to keep existing selections
16812      */
16813     selectLastRow : function(keepExisting){
16814         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16815     },
16816
16817     /**
16818      * Selects the row immediately following the last selected row.
16819      * @param {Boolean} keepExisting (optional) True to keep existing selections
16820      */
16821     selectNext : function(keepExisting){
16822         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16823             this.selectRow(this.last+1, keepExisting);
16824             this.grid.getView().focusRow(this.last);
16825         }
16826     },
16827
16828     /**
16829      * Selects the row that precedes the last selected row.
16830      * @param {Boolean} keepExisting (optional) True to keep existing selections
16831      */
16832     selectPrevious : function(keepExisting){
16833         if(this.last){
16834             this.selectRow(this.last-1, keepExisting);
16835             this.grid.getView().focusRow(this.last);
16836         }
16837     },
16838
16839     /**
16840      * Returns the selected records
16841      * @return {Array} Array of selected records
16842      */
16843     getSelections : function(){
16844         return [].concat(this.selections.items);
16845     },
16846
16847     /**
16848      * Returns the first selected record.
16849      * @return {Record}
16850      */
16851     getSelected : function(){
16852         return this.selections.itemAt(0);
16853     },
16854
16855
16856     /**
16857      * Clears all selections.
16858      */
16859     clearSelections : function(fast){
16860         if(this.locked) return;
16861         if(fast !== true){
16862             var ds = this.grid.dataSource;
16863             var s = this.selections;
16864             s.each(function(r){
16865                 this.deselectRow(ds.indexOfId(r.id));
16866             }, this);
16867             s.clear();
16868         }else{
16869             this.selections.clear();
16870         }
16871         this.last = false;
16872     },
16873
16874
16875     /**
16876      * Selects all rows.
16877      */
16878     selectAll : function(){
16879         if(this.locked) return;
16880         this.selections.clear();
16881         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16882             this.selectRow(i, true);
16883         }
16884     },
16885
16886     /**
16887      * Returns True if there is a selection.
16888      * @return {Boolean}
16889      */
16890     hasSelection : function(){
16891         return this.selections.length > 0;
16892     },
16893
16894     /**
16895      * Returns True if the specified row is selected.
16896      * @param {Number/Record} record The record or index of the record to check
16897      * @return {Boolean}
16898      */
16899     isSelected : function(index){
16900         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16901         return (r && this.selections.key(r.id) ? true : false);
16902     },
16903
16904     /**
16905      * Returns True if the specified record id is selected.
16906      * @param {String} id The id of record to check
16907      * @return {Boolean}
16908      */
16909     isIdSelected : function(id){
16910         return (this.selections.key(id) ? true : false);
16911     },
16912
16913     // private
16914     handleMouseDown : function(e, t){
16915         var view = this.grid.getView(), rowIndex;
16916         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16917             return;
16918         };
16919         if(e.shiftKey && this.last !== false){
16920             var last = this.last;
16921             this.selectRange(last, rowIndex, e.ctrlKey);
16922             this.last = last; // reset the last
16923             view.focusRow(rowIndex);
16924         }else{
16925             var isSelected = this.isSelected(rowIndex);
16926             if(e.button !== 0 && isSelected){
16927                 view.focusRow(rowIndex);
16928             }else if(e.ctrlKey && isSelected){
16929                 this.deselectRow(rowIndex);
16930             }else if(!isSelected){
16931                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16932                 view.focusRow(rowIndex);
16933             }
16934         }
16935         this.fireEvent("afterselectionchange", this);
16936     },
16937     // private
16938     handleDragableRowClick :  function(grid, rowIndex, e) 
16939     {
16940         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16941             this.selectRow(rowIndex, false);
16942             grid.view.focusRow(rowIndex);
16943              this.fireEvent("afterselectionchange", this);
16944         }
16945     },
16946     
16947     /**
16948      * Selects multiple rows.
16949      * @param {Array} rows Array of the indexes of the row to select
16950      * @param {Boolean} keepExisting (optional) True to keep existing selections
16951      */
16952     selectRows : function(rows, keepExisting){
16953         if(!keepExisting){
16954             this.clearSelections();
16955         }
16956         for(var i = 0, len = rows.length; i < len; i++){
16957             this.selectRow(rows[i], true);
16958         }
16959     },
16960
16961     /**
16962      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16963      * @param {Number} startRow The index of the first row in the range
16964      * @param {Number} endRow The index of the last row in the range
16965      * @param {Boolean} keepExisting (optional) True to retain existing selections
16966      */
16967     selectRange : function(startRow, endRow, keepExisting){
16968         if(this.locked) return;
16969         if(!keepExisting){
16970             this.clearSelections();
16971         }
16972         if(startRow <= endRow){
16973             for(var i = startRow; i <= endRow; i++){
16974                 this.selectRow(i, true);
16975             }
16976         }else{
16977             for(var i = startRow; i >= endRow; i--){
16978                 this.selectRow(i, true);
16979             }
16980         }
16981     },
16982
16983     /**
16984      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16985      * @param {Number} startRow The index of the first row in the range
16986      * @param {Number} endRow The index of the last row in the range
16987      */
16988     deselectRange : function(startRow, endRow, preventViewNotify){
16989         if(this.locked) return;
16990         for(var i = startRow; i <= endRow; i++){
16991             this.deselectRow(i, preventViewNotify);
16992         }
16993     },
16994
16995     /**
16996      * Selects a row.
16997      * @param {Number} row The index of the row to select
16998      * @param {Boolean} keepExisting (optional) True to keep existing selections
16999      */
17000     selectRow : function(index, keepExisting, preventViewNotify){
17001         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17002         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17003             if(!keepExisting || this.singleSelect){
17004                 this.clearSelections();
17005             }
17006             var r = this.grid.dataSource.getAt(index);
17007             this.selections.add(r);
17008             this.last = this.lastActive = index;
17009             if(!preventViewNotify){
17010                 this.grid.getView().onRowSelect(index);
17011             }
17012             this.fireEvent("rowselect", this, index, r);
17013             this.fireEvent("selectionchange", this);
17014         }
17015     },
17016
17017     /**
17018      * Deselects a row.
17019      * @param {Number} row The index of the row to deselect
17020      */
17021     deselectRow : function(index, preventViewNotify){
17022         if(this.locked) return;
17023         if(this.last == index){
17024             this.last = false;
17025         }
17026         if(this.lastActive == index){
17027             this.lastActive = false;
17028         }
17029         var r = this.grid.dataSource.getAt(index);
17030         this.selections.remove(r);
17031         if(!preventViewNotify){
17032             this.grid.getView().onRowDeselect(index);
17033         }
17034         this.fireEvent("rowdeselect", this, index);
17035         this.fireEvent("selectionchange", this);
17036     },
17037
17038     // private
17039     restoreLast : function(){
17040         if(this._last){
17041             this.last = this._last;
17042         }
17043     },
17044
17045     // private
17046     acceptsNav : function(row, col, cm){
17047         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17048     },
17049
17050     // private
17051     onEditorKey : function(field, e){
17052         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17053         if(k == e.TAB){
17054             e.stopEvent();
17055             ed.completeEdit();
17056             if(e.shiftKey){
17057                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17058             }else{
17059                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17060             }
17061         }else if(k == e.ENTER && !e.ctrlKey){
17062             e.stopEvent();
17063             ed.completeEdit();
17064             if(e.shiftKey){
17065                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17066             }else{
17067                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17068             }
17069         }else if(k == e.ESC){
17070             ed.cancelEdit();
17071         }
17072         if(newCell){
17073             g.startEditing(newCell[0], newCell[1]);
17074         }
17075     }
17076 });/*
17077  * - LGPL
17078  *
17079  * element
17080  * 
17081  */
17082
17083 /**
17084  * @class Roo.bootstrap.MessageBar
17085  * @extends Roo.bootstrap.Component
17086  * Bootstrap MessageBar class
17087  * @cfg {String} html contents of the MessageBar
17088  * @cfg {String} weight (info | success | warning | danger) default info
17089  * @cfg {String} beforeClass insert the bar before the given class
17090  * @cfg {Boolean} closable (true | false) default false
17091  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17092  * 
17093  * @constructor
17094  * Create a new Element
17095  * @param {Object} config The config object
17096  */
17097
17098 Roo.bootstrap.MessageBar = function(config){
17099     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17100 };
17101
17102 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17103     
17104     html: '',
17105     weight: 'info',
17106     closable: false,
17107     fixed: false,
17108     beforeClass: 'bootstrap-sticky-wrap',
17109     
17110     getAutoCreate : function(){
17111         
17112         var cfg = {
17113             tag: 'div',
17114             cls: 'alert alert-dismissable alert-' + this.weight,
17115             cn: [
17116                 {
17117                     tag: 'span',
17118                     cls: 'message',
17119                     html: this.html || ''
17120                 }
17121             ]
17122         }
17123         
17124         if(this.fixed){
17125             cfg.cls += ' alert-messages-fixed';
17126         }
17127         
17128         if(this.closable){
17129             cfg.cn.push({
17130                 tag: 'button',
17131                 cls: 'close',
17132                 html: 'x'
17133             });
17134         }
17135         
17136         return cfg;
17137     },
17138     
17139     onRender : function(ct, position)
17140     {
17141         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17142         
17143         if(!this.el){
17144             var cfg = Roo.apply({},  this.getAutoCreate());
17145             cfg.id = Roo.id();
17146             
17147             if (this.cls) {
17148                 cfg.cls += ' ' + this.cls;
17149             }
17150             if (this.style) {
17151                 cfg.style = this.style;
17152             }
17153             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17154             
17155             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17156         }
17157         
17158         this.el.select('>button.close').on('click', this.hide, this);
17159         
17160     },
17161     
17162     show : function()
17163     {
17164         if (!this.rendered) {
17165             this.render();
17166         }
17167         
17168         this.el.show();
17169         
17170         this.fireEvent('show', this);
17171         
17172     },
17173     
17174     hide : function()
17175     {
17176         if (!this.rendered) {
17177             this.render();
17178         }
17179         
17180         this.el.hide();
17181         
17182         this.fireEvent('hide', this);
17183     },
17184     
17185     update : function()
17186     {
17187 //        var e = this.el.dom.firstChild;
17188 //        
17189 //        if(this.closable){
17190 //            e = e.nextSibling;
17191 //        }
17192 //        
17193 //        e.data = this.html || '';
17194
17195         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17196     }
17197    
17198 });
17199
17200  
17201
17202