roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298     if (this.cls && this.cls.length) {
299         Roo.get(document.body).addClass(this.cls);
300     }
301 };
302
303 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
304       
305         autoCreate : {
306         cls: 'container'
307     },
308     onRender : function(ct, position)
309     {
310        /* Roo.log("Roo.bootstrap.Body - onRender");
311         if (this.cls && this.cls.length) {
312             Roo.get(document.body).addClass(this.cls);
313         }
314         // style??? xttr???
315         */
316     }
317     
318     
319  
320    
321 });
322
323  /*
324  * - LGPL
325  *
326  * button group
327  * 
328  */
329
330
331 /**
332  * @class Roo.bootstrap.ButtonGroup
333  * @extends Roo.bootstrap.Component
334  * Bootstrap ButtonGroup class
335  * @cfg {String} size lg | sm | xs (default empty normal)
336  * @cfg {String} align vertical | justified  (default none)
337  * @cfg {String} direction up | down (default down)
338  * @cfg {Boolean} toolbar false | true
339  * @cfg {Boolean} btn true | false
340  * 
341  * 
342  * @constructor
343  * Create a new Input
344  * @param {Object} config The config object
345  */
346
347 Roo.bootstrap.ButtonGroup = function(config){
348     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
349 };
350
351 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
352     
353     size: '',
354     align: '',
355     direction: '',
356     toolbar: false,
357     btn: true,
358
359     getAutoCreate : function(){
360         var cfg = {
361             cls: 'btn-group',
362             html : null
363         }
364         
365         cfg.html = this.html || cfg.html;
366         
367         if (this.toolbar) {
368             cfg = {
369                 cls: 'btn-toolbar',
370                 html: null
371             }
372             
373             return cfg;
374         }
375         
376         if (['vertical','justified'].indexOf(this.align)!==-1) {
377             cfg.cls = 'btn-group-' + this.align;
378             
379             if (this.align == 'justified') {
380                 console.log(this.items);
381             }
382         }
383         
384         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
385             cfg.cls += ' btn-group-' + this.size;
386         }
387         
388         if (this.direction == 'up') {
389             cfg.cls += ' dropup' ;
390         }
391         
392         return cfg;
393     }
394    
395 });
396
397  /*
398  * - LGPL
399  *
400  * button
401  * 
402  */
403
404 /**
405  * @class Roo.bootstrap.Button
406  * @extends Roo.bootstrap.Component
407  * Bootstrap Button class
408  * @cfg {String} html The button content
409  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
410  * @cfg {String} size empty | lg | sm | xs
411  * @cfg {String} tag empty | a | input | submit
412  * @cfg {String} href empty or href
413  * @cfg {Boolean} disabled false | true
414  * @cfg {Boolean} isClose false | true
415  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
416  * @cfg {String} badge text for badge
417  * @cfg {String} theme default (or empty) | glow
418  * @cfg {Boolean} inverse false | true
419  * @cfg {Boolean} toggle false | true
420  * @cfg {String} ontext text for on toggle state
421  * @cfg {String} offtext text for off toggle state
422  * @cfg {Boolean} defaulton true | false
423  * @cfg {Boolean} preventDefault (true | false) default true
424  * @cfg {Boolean} removeClass true | false remove the standard class..
425  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
426  * 
427  * @constructor
428  * Create a new button
429  * @param {Object} config The config object
430  */
431
432
433 Roo.bootstrap.Button = function(config){
434     Roo.bootstrap.Button.superclass.constructor.call(this, config);
435     this.addEvents({
436         // raw events
437         /**
438          * @event click
439          * When a butotn is pressed
440          * @param {Roo.EventObject} e
441          */
442         "click" : true,
443          /**
444          * @event toggle
445          * After the button has been toggles
446          * @param {Roo.EventObject} e
447          * @param {boolean} pressed (also available as button.pressed)
448          */
449         "toggle" : true
450     });
451 };
452
453 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
454     html: false,
455     active: false,
456     weight: '',
457     size: '',
458     tag: 'button',
459     href: '',
460     disabled: false,
461     isClose: false,
462     glyphicon: '',
463     badge: '',
464     theme: 'default',
465     inverse: false,
466     
467     toggle: false,
468     ontext: 'ON',
469     offtext: 'OFF',
470     defaulton: true,
471     preventDefault: true,
472     removeClass: false,
473     name: false,
474     target: false,
475     
476     
477     pressed : null,
478      
479     
480     getAutoCreate : function(){
481         
482         var cfg = {
483             tag : 'button',
484             cls : 'roo-button',
485             html: ''
486         };
487         
488         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
489             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
490             this.tag = 'button';
491         } else {
492             cfg.tag = this.tag;
493         }
494         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
495         
496         if (this.toggle == true) {
497             cfg={
498                 tag: 'div',
499                 cls: 'slider-frame roo-button',
500                 cn: [
501                     {
502                         tag: 'span',
503                         'data-on-text':'ON',
504                         'data-off-text':'OFF',
505                         cls: 'slider-button',
506                         html: this.offtext
507                     }
508                 ]
509             };
510             
511             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
512                 cfg.cls += ' '+this.weight;
513             }
514             
515             return cfg;
516         }
517         
518         if (this.isClose) {
519             cfg.cls += ' close';
520             
521             cfg["aria-hidden"] = true;
522             
523             cfg.html = "&times;";
524             
525             return cfg;
526         }
527         
528          
529         if (this.theme==='default') {
530             cfg.cls = 'btn roo-button';
531             
532             //if (this.parentType != 'Navbar') {
533             this.weight = this.weight.length ?  this.weight : 'default';
534             //}
535             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
536                 
537                 cfg.cls += ' btn-' + this.weight;
538             }
539         } else if (this.theme==='glow') {
540             
541             cfg.tag = 'a';
542             cfg.cls = 'btn-glow roo-button';
543             
544             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
545                 
546                 cfg.cls += ' ' + this.weight;
547             }
548         }
549    
550         
551         if (this.inverse) {
552             this.cls += ' inverse';
553         }
554         
555         
556         if (this.active) {
557             cfg.cls += ' active';
558         }
559         
560         if (this.disabled) {
561             cfg.disabled = 'disabled';
562         }
563         
564         if (this.items) {
565             Roo.log('changing to ul' );
566             cfg.tag = 'ul';
567             this.glyphicon = 'caret';
568         }
569         
570         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
571          
572         //gsRoo.log(this.parentType);
573         if (this.parentType === 'Navbar' && !this.parent().bar) {
574             Roo.log('changing to li?');
575             
576             cfg.tag = 'li';
577             
578             cfg.cls = '';
579             cfg.cn =  [{
580                 tag : 'a',
581                 cls : 'roo-button',
582                 html : this.html,
583                 href : this.href || '#'
584             }];
585             if (this.menu) {
586                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
587                 cfg.cls += ' dropdown';
588             }   
589             
590             delete cfg.html;
591             
592         }
593         
594        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
595         
596         if (this.glyphicon) {
597             cfg.html = ' ' + cfg.html;
598             
599             cfg.cn = [
600                 {
601                     tag: 'span',
602                     cls: 'glyphicon glyphicon-' + this.glyphicon
603                 }
604             ];
605         }
606         
607         if (this.badge) {
608             cfg.html += ' ';
609             
610             cfg.tag = 'a';
611             
612 //            cfg.cls='btn roo-button';
613             
614             cfg.href=this.href;
615             
616             var value = cfg.html;
617             
618             if(this.glyphicon){
619                 value = {
620                             tag: 'span',
621                             cls: 'glyphicon glyphicon-' + this.glyphicon,
622                             html: this.html
623                         };
624                 
625             }
626             
627             cfg.cn = [
628                 value,
629                 {
630                     tag: 'span',
631                     cls: 'badge',
632                     html: this.badge
633                 }
634             ];
635             
636             cfg.html='';
637         }
638         
639         if (this.menu) {
640             cfg.cls += ' dropdown';
641             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
642         }
643         
644         if (cfg.tag !== 'a' && this.href !== '') {
645             throw "Tag must be a to set href.";
646         } else if (this.href.length > 0) {
647             cfg.href = this.href;
648         }
649         
650         if(this.removeClass){
651             cfg.cls = '';
652         }
653         
654         if(this.target){
655             cfg.target = this.target;
656         }
657         
658         return cfg;
659     },
660     initEvents: function() {
661        // Roo.log('init events?');
662 //        Roo.log(this.el.dom);
663        if (this.el.hasClass('roo-button')) {
664             this.el.on('click', this.onClick, this);
665        } else {
666             this.el.select('.roo-button').on('click', this.onClick, this);
667        }
668        
669        if(this.removeClass){
670            this.el.on('click', this.onClick, this);
671        }
672        
673        this.el.enableDisplayMode();
674         
675     },
676     onClick : function(e)
677     {
678         if (this.disabled) {
679             return;
680         }
681         
682         Roo.log('button on click ');
683         if(this.preventDefault){
684             e.preventDefault();
685         }
686         if (this.pressed === true || this.pressed === false) {
687             this.pressed = !this.pressed;
688             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
689             this.fireEvent('toggle', this, e, this.pressed);
690         }
691         
692         
693         this.fireEvent('click', this, e);
694     },
695     
696     /**
697      * Enables this button
698      */
699     enable : function()
700     {
701         this.disabled = false;
702         this.el.removeClass('disabled');
703     },
704     
705     /**
706      * Disable this button
707      */
708     disable : function()
709     {
710         this.disabled = true;
711         this.el.addClass('disabled');
712     },
713      /**
714      * sets the active state on/off, 
715      * @param {Boolean} state (optional) Force a particular state
716      */
717     setActive : function(v) {
718         
719         this.el[v ? 'addClass' : 'removeClass']('active');
720     },
721      /**
722      * toggles the current active state 
723      */
724     toggleActive : function()
725     {
726        var active = this.el.hasClass('active');
727        this.setActive(!active);
728        
729         
730     },
731     setText : function(str)
732     {
733         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
734     },
735     hide: function() {
736        
737      
738         this.el.hide();   
739     },
740     show: function() {
741        
742         this.el.show();   
743     }
744     
745     
746 });
747
748  /*
749  * - LGPL
750  *
751  * column
752  * 
753  */
754
755 /**
756  * @class Roo.bootstrap.Column
757  * @extends Roo.bootstrap.Component
758  * Bootstrap Column class
759  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
760  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
761  * @cfg {Number} md colspan out of 12 for computer-sized screens
762  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
763  * @cfg {String} html content of column.
764  * 
765  * @constructor
766  * Create a new Column
767  * @param {Object} config The config object
768  */
769
770 Roo.bootstrap.Column = function(config){
771     Roo.bootstrap.Column.superclass.constructor.call(this, config);
772 };
773
774 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
775     
776     xs: null,
777     sm: null,
778     md: null,
779     lg: null,
780     html: '',
781     offset: 0,
782     
783     getAutoCreate : function(){
784         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
785         
786         cfg = {
787             tag: 'div',
788             cls: 'column'
789         };
790         
791         var settings=this;
792         ['xs','sm','md','lg'].map(function(size){
793             if (settings[size]) {
794                 cfg.cls += ' col-' + size + '-' + settings[size];
795             }
796         });
797         if (this.html.length) {
798             cfg.html = this.html;
799         }
800         
801         return cfg;
802     }
803    
804 });
805
806  
807
808  /*
809  * - LGPL
810  *
811  * page container.
812  * 
813  */
814
815
816 /**
817  * @class Roo.bootstrap.Container
818  * @extends Roo.bootstrap.Component
819  * Bootstrap Container class
820  * @cfg {Boolean} jumbotron is it a jumbotron element
821  * @cfg {String} html content of element
822  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
823  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
824  * @cfg {String} header content of header (for panel)
825  * @cfg {String} footer content of footer (for panel)
826  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
827  * @cfg {String} tag (header|aside|section) type of HTML tag.
828
829  *     
830  * @constructor
831  * Create a new Container
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Container = function(config){
836     Roo.bootstrap.Container.superclass.constructor.call(this, config);
837 };
838
839 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
840     
841     jumbotron : false,
842     well: '',
843     panel : '',
844     header: '',
845     footer : '',
846     sticky: '',
847     tag : false,
848   
849      
850     getChildContainer : function() {
851         
852         if(!this.el){
853             return false;
854         }
855         
856         if (this.panel.length) {
857             return this.el.select('.panel-body',true).first();
858         }
859         
860         return this.el;
861     },
862     
863     
864     getAutoCreate : function(){
865         
866         var cfg = {
867             tag : this.tag || 'div',
868             html : '',
869             cls : ''
870         };
871         if (this.jumbotron) {
872             cfg.cls = 'jumbotron';
873         }
874         // - this is applied by the parent..
875         //if (this.cls) {
876         //    cfg.cls = this.cls + '';
877         //}
878         
879         if (this.sticky.length) {
880             
881             var bd = Roo.get(document.body);
882             if (!bd.hasClass('bootstrap-sticky')) {
883                 bd.addClass('bootstrap-sticky');
884                 Roo.select('html',true).setStyle('height', '100%');
885             }
886              
887             cfg.cls += 'bootstrap-sticky-' + this.sticky;
888         }
889         
890         
891         if (this.well.length) {
892             switch (this.well) {
893                 case 'lg':
894                 case 'sm':
895                     cfg.cls +=' well well-' +this.well;
896                     break;
897                 default:
898                     cfg.cls +=' well';
899                     break;
900             }
901         }
902         
903         var body = cfg;
904         
905         if (this.panel.length) {
906             cfg.cls += ' panel panel-' + this.panel;
907             cfg.cn = [];
908             if (this.header.length) {
909                 cfg.cn.push({
910                     
911                     cls : 'panel-heading',
912                     cn : [{
913                         tag: 'h3',
914                         cls : 'panel-title',
915                         html : this.header
916                     }]
917                     
918                 });
919             }
920             body = false;
921             cfg.cn.push({
922                 cls : 'panel-body',
923                 html : this.html
924             });
925             
926             
927             if (this.footer.length) {
928                 cfg.cn.push({
929                     cls : 'panel-footer',
930                     html : this.footer
931                     
932                 });
933             }
934             
935         }
936         
937         if (body) {
938             body.html = this.html || cfg.html;
939         }
940         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
941             cfg.cls =  'container';
942         }
943         
944         return cfg;
945     }
946    
947 });
948
949  /*
950  * - LGPL
951  *
952  * image
953  * 
954  */
955
956
957 /**
958  * @class Roo.bootstrap.Img
959  * @extends Roo.bootstrap.Component
960  * Bootstrap Img class
961  * @cfg {Boolean} imgResponsive false | true
962  * @cfg {String} border rounded | circle | thumbnail
963  * @cfg {String} src image source
964  * @cfg {String} alt image alternative text
965  * @cfg {String} href a tag href
966  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
967  * 
968  * @constructor
969  * Create a new Input
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Img = function(config){
974     Roo.bootstrap.Img.superclass.constructor.call(this, config);
975     
976     this.addEvents({
977         // img events
978         /**
979          * @event click
980          * The img click event for the img.
981          * @param {Roo.EventObject} e
982          */
983         "click" : true
984     });
985 };
986
987 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
988     
989     imgResponsive: true,
990     border: '',
991     src: '',
992     href: false,
993     target: false,
994
995     getAutoCreate : function(){
996         
997         var cfg = {
998             tag: 'img',
999             cls: (this.imgResponsive) ? 'img-responsive' : '',
1000             html : null
1001         }
1002         
1003         cfg.html = this.html || cfg.html;
1004         
1005         cfg.src = this.src || cfg.src;
1006         
1007         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1008             cfg.cls += ' img-' + this.border;
1009         }
1010         
1011         if(this.alt){
1012             cfg.alt = this.alt;
1013         }
1014         
1015         if(this.href){
1016             var a = {
1017                 tag: 'a',
1018                 href: this.href,
1019                 cn: [
1020                     cfg
1021                 ]
1022             }
1023             
1024             if(this.target){
1025                 a.target = this.target;
1026             }
1027             
1028         }
1029         
1030         
1031         return (this.href) ? a : cfg;
1032     },
1033     
1034     initEvents: function() {
1035         
1036         if(!this.href){
1037             this.el.on('click', this.onClick, this);
1038         }
1039     },
1040     
1041     onClick : function(e)
1042     {
1043         Roo.log('img onclick');
1044         this.fireEvent('click', this, e);
1045     }
1046    
1047 });
1048
1049  /*
1050  * - LGPL
1051  *
1052  * image
1053  * 
1054  */
1055
1056
1057 /**
1058  * @class Roo.bootstrap.Link
1059  * @extends Roo.bootstrap.Component
1060  * Bootstrap Link Class
1061  * @cfg {String} alt image alternative text
1062  * @cfg {String} href a tag href
1063  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1064  * @cfg {String} html the content of the link.
1065
1066  * 
1067  * @constructor
1068  * Create a new Input
1069  * @param {Object} config The config object
1070  */
1071
1072 Roo.bootstrap.Link = function(config){
1073     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1074     
1075     this.addEvents({
1076         // img events
1077         /**
1078          * @event click
1079          * The img click event for the img.
1080          * @param {Roo.EventObject} e
1081          */
1082         "click" : true
1083     });
1084 };
1085
1086 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1087     
1088     href: false,
1089     target: false,
1090
1091     getAutoCreate : function(){
1092         
1093         var cfg = {
1094             tag: 'a',
1095             html : this.html || 'html-missing'
1096         }
1097         
1098         
1099         if(this.alt){
1100             cfg.alt = this.alt;
1101         }
1102         cfg.href = this.href || '#';
1103         if(this.target){
1104             cfg.target = this.target;
1105         }
1106         
1107         return cfg;
1108     },
1109     
1110     initEvents: function() {
1111         
1112         if(!this.href){
1113             this.el.on('click', this.onClick, this);
1114         }
1115     },
1116     
1117     onClick : function(e)
1118     {
1119         //Roo.log('img onclick');
1120         this.fireEvent('click', this, e);
1121     }
1122    
1123 });
1124
1125  /*
1126  * - LGPL
1127  *
1128  * header
1129  * 
1130  */
1131
1132 /**
1133  * @class Roo.bootstrap.Header
1134  * @extends Roo.bootstrap.Component
1135  * Bootstrap Header class
1136  * @cfg {String} html content of header
1137  * @cfg {Number} level (1|2|3|4|5|6) default 1
1138  * 
1139  * @constructor
1140  * Create a new Header
1141  * @param {Object} config The config object
1142  */
1143
1144
1145 Roo.bootstrap.Header  = function(config){
1146     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1147 };
1148
1149 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1150     
1151     //href : false,
1152     html : false,
1153     level : 1,
1154     
1155     
1156     
1157     getAutoCreate : function(){
1158         
1159         var cfg = {
1160             tag: 'h' + (1 *this.level),
1161             html: this.html || 'fill in html'
1162         } ;
1163         
1164         return cfg;
1165     }
1166    
1167 });
1168
1169  
1170
1171  /*
1172  * Based on:
1173  * Ext JS Library 1.1.1
1174  * Copyright(c) 2006-2007, Ext JS, LLC.
1175  *
1176  * Originally Released Under LGPL - original licence link has changed is not relivant.
1177  *
1178  * Fork - LGPL
1179  * <script type="text/javascript">
1180  */
1181  
1182 /**
1183  * @class Roo.bootstrap.MenuMgr
1184  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1185  * @singleton
1186  */
1187 Roo.bootstrap.MenuMgr = function(){
1188    var menus, active, groups = {}, attached = false, lastShow = new Date();
1189
1190    // private - called when first menu is created
1191    function init(){
1192        menus = {};
1193        active = new Roo.util.MixedCollection();
1194        Roo.get(document).addKeyListener(27, function(){
1195            if(active.length > 0){
1196                hideAll();
1197            }
1198        });
1199    }
1200
1201    // private
1202    function hideAll(){
1203        if(active && active.length > 0){
1204            var c = active.clone();
1205            c.each(function(m){
1206                m.hide();
1207            });
1208        }
1209    }
1210
1211    // private
1212    function onHide(m){
1213        active.remove(m);
1214        if(active.length < 1){
1215            Roo.get(document).un("mouseup", onMouseDown);
1216             
1217            attached = false;
1218        }
1219    }
1220
1221    // private
1222    function onShow(m){
1223        var last = active.last();
1224        lastShow = new Date();
1225        active.add(m);
1226        if(!attached){
1227           Roo.get(document).on("mouseup", onMouseDown);
1228            
1229            attached = true;
1230        }
1231        if(m.parentMenu){
1232           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1233           m.parentMenu.activeChild = m;
1234        }else if(last && last.isVisible()){
1235           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1236        }
1237    }
1238
1239    // private
1240    function onBeforeHide(m){
1241        if(m.activeChild){
1242            m.activeChild.hide();
1243        }
1244        if(m.autoHideTimer){
1245            clearTimeout(m.autoHideTimer);
1246            delete m.autoHideTimer;
1247        }
1248    }
1249
1250    // private
1251    function onBeforeShow(m){
1252        var pm = m.parentMenu;
1253        if(!pm && !m.allowOtherMenus){
1254            hideAll();
1255        }else if(pm && pm.activeChild && active != m){
1256            pm.activeChild.hide();
1257        }
1258    }
1259
1260    // private
1261    function onMouseDown(e){
1262         Roo.log("on MouseDown");
1263         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1264            hideAll();
1265         }
1266         
1267         
1268    }
1269
1270    // private
1271    function onBeforeCheck(mi, state){
1272        if(state){
1273            var g = groups[mi.group];
1274            for(var i = 0, l = g.length; i < l; i++){
1275                if(g[i] != mi){
1276                    g[i].setChecked(false);
1277                }
1278            }
1279        }
1280    }
1281
1282    return {
1283
1284        /**
1285         * Hides all menus that are currently visible
1286         */
1287        hideAll : function(){
1288             hideAll();  
1289        },
1290
1291        // private
1292        register : function(menu){
1293            if(!menus){
1294                init();
1295            }
1296            menus[menu.id] = menu;
1297            menu.on("beforehide", onBeforeHide);
1298            menu.on("hide", onHide);
1299            menu.on("beforeshow", onBeforeShow);
1300            menu.on("show", onShow);
1301            var g = menu.group;
1302            if(g && menu.events["checkchange"]){
1303                if(!groups[g]){
1304                    groups[g] = [];
1305                }
1306                groups[g].push(menu);
1307                menu.on("checkchange", onCheck);
1308            }
1309        },
1310
1311         /**
1312          * Returns a {@link Roo.menu.Menu} object
1313          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1314          * be used to generate and return a new Menu instance.
1315          */
1316        get : function(menu){
1317            if(typeof menu == "string"){ // menu id
1318                return menus[menu];
1319            }else if(menu.events){  // menu instance
1320                return menu;
1321            }
1322            /*else if(typeof menu.length == 'number'){ // array of menu items?
1323                return new Roo.bootstrap.Menu({items:menu});
1324            }else{ // otherwise, must be a config
1325                return new Roo.bootstrap.Menu(menu);
1326            }
1327            */
1328            return false;
1329        },
1330
1331        // private
1332        unregister : function(menu){
1333            delete menus[menu.id];
1334            menu.un("beforehide", onBeforeHide);
1335            menu.un("hide", onHide);
1336            menu.un("beforeshow", onBeforeShow);
1337            menu.un("show", onShow);
1338            var g = menu.group;
1339            if(g && menu.events["checkchange"]){
1340                groups[g].remove(menu);
1341                menu.un("checkchange", onCheck);
1342            }
1343        },
1344
1345        // private
1346        registerCheckable : function(menuItem){
1347            var g = menuItem.group;
1348            if(g){
1349                if(!groups[g]){
1350                    groups[g] = [];
1351                }
1352                groups[g].push(menuItem);
1353                menuItem.on("beforecheckchange", onBeforeCheck);
1354            }
1355        },
1356
1357        // private
1358        unregisterCheckable : function(menuItem){
1359            var g = menuItem.group;
1360            if(g){
1361                groups[g].remove(menuItem);
1362                menuItem.un("beforecheckchange", onBeforeCheck);
1363            }
1364        }
1365    };
1366 }();/*
1367  * - LGPL
1368  *
1369  * menu
1370  * 
1371  */
1372
1373 /**
1374  * @class Roo.bootstrap.Menu
1375  * @extends Roo.bootstrap.Component
1376  * Bootstrap Menu class - container for MenuItems
1377  * @cfg {String} type (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.EventObject} e
12185              * @param {Roo.bootstrap.TabPanel} this
12186              * @param {Boolean} state the new state
12187             
12188          */
12189         'changed': true
12190      });
12191 };
12192
12193 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12194     
12195     active: false,
12196     html: false,
12197     tabId: false,
12198     navId : false,
12199     
12200     getAutoCreate : function(){
12201         var cfg = {
12202             tag: 'div',
12203             cls: 'tab-pane',
12204             html: this.html || ''
12205         };
12206         
12207         if(this.active){
12208             cfg.cls += ' active';
12209         }
12210         
12211         if(this.tabId){
12212             cfg.tabId = this.tabId;
12213         }
12214         
12215         return cfg;
12216     },
12217     onRender : function(ct, position)
12218     {
12219        // Roo.log("Call onRender: " + this.xtype);
12220         
12221         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12222         
12223         if (this.navId && this.tabId) {
12224             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12225             if (!item) {
12226                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12227             } else {
12228                 item.on('changed', function(e, item, state) {
12229                     Roo.log(item);
12230                     Roo.log(state);
12231                     Roo.log(e);
12232                     this.setActive(state);
12233                 }, this);
12234             }
12235         }
12236         
12237     },
12238     setActive: function(state)
12239     {
12240         Roo.log("panel - set active " + this.tabId + "=" + state);
12241         
12242         this.active = state;
12243         if (!state) {
12244             this.el.removeClass('active');
12245             
12246         } else  if (!this.el.hasClass('active')) {
12247             this.el.addClass('active');
12248         }
12249         this.fireEvent('changed', this, state);
12250     }
12251     
12252     
12253 });
12254  
12255
12256  
12257
12258  /*
12259  * - LGPL
12260  *
12261  * DateField
12262  * 
12263  */
12264
12265 /**
12266  * @class Roo.bootstrap.DateField
12267  * @extends Roo.bootstrap.Input
12268  * Bootstrap DateField class
12269  * @cfg {Number} weekStart default 0
12270  * @cfg {Number} weekStart default 0
12271  * @cfg {Number} viewMode default empty, (months|years)
12272  * @cfg {Number} minViewMode default empty, (months|years)
12273  * @cfg {Number} startDate default -Infinity
12274  * @cfg {Number} endDate default Infinity
12275  * @cfg {Boolean} todayHighlight default false
12276  * @cfg {Boolean} todayBtn default false
12277  * @cfg {Boolean} calendarWeeks default false
12278  * @cfg {Object} daysOfWeekDisabled default empty
12279  * 
12280  * @cfg {Boolean} keyboardNavigation default true
12281  * @cfg {String} language default en
12282  * 
12283  * @constructor
12284  * Create a new DateField
12285  * @param {Object} config The config object
12286  */
12287
12288 Roo.bootstrap.DateField = function(config){
12289     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12290      this.addEvents({
12291             /**
12292              * @event show
12293              * Fires when this field show.
12294              * @param {Roo.bootstrap.DateField} this
12295              * @param {Mixed} date The date value
12296              */
12297             show : true,
12298             /**
12299              * @event show
12300              * Fires when this field hide.
12301              * @param {Roo.bootstrap.DateField} this
12302              * @param {Mixed} date The date value
12303              */
12304             hide : true,
12305             /**
12306              * @event select
12307              * Fires when select a date.
12308              * @param {Roo.bootstrap.DateField} this
12309              * @param {Mixed} date The date value
12310              */
12311             select : true
12312         });
12313 };
12314
12315 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12316     
12317     /**
12318      * @cfg {String} format
12319      * The default date format string which can be overriden for localization support.  The format must be
12320      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12321      */
12322     format : "m/d/y",
12323     /**
12324      * @cfg {String} altFormats
12325      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12326      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12327      */
12328     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12329     
12330     weekStart : 0,
12331     
12332     viewMode : '',
12333     
12334     minViewMode : '',
12335     
12336     todayHighlight : false,
12337     
12338     todayBtn: false,
12339     
12340     language: 'en',
12341     
12342     keyboardNavigation: true,
12343     
12344     calendarWeeks: false,
12345     
12346     startDate: -Infinity,
12347     
12348     endDate: Infinity,
12349     
12350     daysOfWeekDisabled: [],
12351     
12352     _events: [],
12353     
12354     UTCDate: function()
12355     {
12356         return new Date(Date.UTC.apply(Date, arguments));
12357     },
12358     
12359     UTCToday: function()
12360     {
12361         var today = new Date();
12362         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12363     },
12364     
12365     getDate: function() {
12366             var d = this.getUTCDate();
12367             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12368     },
12369     
12370     getUTCDate: function() {
12371             return this.date;
12372     },
12373     
12374     setDate: function(d) {
12375             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12376     },
12377     
12378     setUTCDate: function(d) {
12379             this.date = d;
12380             this.setValue(this.formatDate(this.date));
12381     },
12382         
12383     onRender: function(ct, position)
12384     {
12385         
12386         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12387         
12388         this.language = this.language || 'en';
12389         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12390         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12391         
12392         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12393         this.format = this.format || 'm/d/y';
12394         this.isInline = false;
12395         this.isInput = true;
12396         this.component = this.el.select('.add-on', true).first() || false;
12397         this.component = (this.component && this.component.length === 0) ? false : this.component;
12398         this.hasInput = this.component && this.inputEL().length;
12399         
12400         if (typeof(this.minViewMode === 'string')) {
12401             switch (this.minViewMode) {
12402                 case 'months':
12403                     this.minViewMode = 1;
12404                     break;
12405                 case 'years':
12406                     this.minViewMode = 2;
12407                     break;
12408                 default:
12409                     this.minViewMode = 0;
12410                     break;
12411             }
12412         }
12413         
12414         if (typeof(this.viewMode === 'string')) {
12415             switch (this.viewMode) {
12416                 case 'months':
12417                     this.viewMode = 1;
12418                     break;
12419                 case 'years':
12420                     this.viewMode = 2;
12421                     break;
12422                 default:
12423                     this.viewMode = 0;
12424                     break;
12425             }
12426         }
12427                 
12428         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12429         
12430         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12431         
12432         this.picker().on('mousedown', this.onMousedown, this);
12433         this.picker().on('click', this.onClick, this);
12434         
12435         this.picker().addClass('datepicker-dropdown');
12436         
12437         this.startViewMode = this.viewMode;
12438         
12439         
12440         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12441             if(!this.calendarWeeks){
12442                 v.remove();
12443                 return;
12444             };
12445             
12446             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12447             v.attr('colspan', function(i, val){
12448                 return parseInt(val) + 1;
12449             });
12450         })
12451                         
12452         
12453         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12454         
12455         this.setStartDate(this.startDate);
12456         this.setEndDate(this.endDate);
12457         
12458         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12459         
12460         this.fillDow();
12461         this.fillMonths();
12462         this.update();
12463         this.showMode();
12464         
12465         if(this.isInline) {
12466             this.show();
12467         }
12468     },
12469     
12470     picker : function()
12471     {
12472         return this.el.select('.datepicker', true).first();
12473     },
12474     
12475     fillDow: function()
12476     {
12477         var dowCnt = this.weekStart;
12478         
12479         var dow = {
12480             tag: 'tr',
12481             cn: [
12482                 
12483             ]
12484         };
12485         
12486         if(this.calendarWeeks){
12487             dow.cn.push({
12488                 tag: 'th',
12489                 cls: 'cw',
12490                 html: '&nbsp;'
12491             })
12492         }
12493         
12494         while (dowCnt < this.weekStart + 7) {
12495             dow.cn.push({
12496                 tag: 'th',
12497                 cls: 'dow',
12498                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12499             });
12500         }
12501         
12502         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12503     },
12504     
12505     fillMonths: function()
12506     {    
12507         var i = 0
12508         var months = this.picker().select('>.datepicker-months td', true).first();
12509         
12510         months.dom.innerHTML = '';
12511         
12512         while (i < 12) {
12513             var month = {
12514                 tag: 'span',
12515                 cls: 'month',
12516                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12517             }
12518             
12519             months.createChild(month);
12520         }
12521         
12522     },
12523     
12524     update: function(){
12525         
12526         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12527         
12528         if (this.date < this.startDate) {
12529             this.viewDate = new Date(this.startDate);
12530         } else if (this.date > this.endDate) {
12531             this.viewDate = new Date(this.endDate);
12532         } else {
12533             this.viewDate = new Date(this.date);
12534         }
12535         
12536         this.fill();
12537     },
12538     
12539     fill: function() {
12540         var d = new Date(this.viewDate),
12541                 year = d.getUTCFullYear(),
12542                 month = d.getUTCMonth(),
12543                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12544                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12545                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12546                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12547                 currentDate = this.date && this.date.valueOf(),
12548                 today = this.UTCToday();
12549         
12550         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12551         
12552 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12553         
12554 //        this.picker.select('>tfoot th.today').
12555 //                                              .text(dates[this.language].today)
12556 //                                              .toggle(this.todayBtn !== false);
12557     
12558         this.updateNavArrows();
12559         this.fillMonths();
12560                                                 
12561         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12562         
12563         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12564          
12565         prevMonth.setUTCDate(day);
12566         
12567         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12568         
12569         var nextMonth = new Date(prevMonth);
12570         
12571         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12572         
12573         nextMonth = nextMonth.valueOf();
12574         
12575         var fillMonths = false;
12576         
12577         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12578         
12579         while(prevMonth.valueOf() < nextMonth) {
12580             var clsName = '';
12581             
12582             if (prevMonth.getUTCDay() === this.weekStart) {
12583                 if(fillMonths){
12584                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12585                 }
12586                     
12587                 fillMonths = {
12588                     tag: 'tr',
12589                     cn: []
12590                 };
12591                 
12592                 if(this.calendarWeeks){
12593                     // ISO 8601: First week contains first thursday.
12594                     // ISO also states week starts on Monday, but we can be more abstract here.
12595                     var
12596                     // Start of current week: based on weekstart/current date
12597                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12598                     // Thursday of this week
12599                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12600                     // First Thursday of year, year from thursday
12601                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12602                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12603                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12604                     
12605                     fillMonths.cn.push({
12606                         tag: 'td',
12607                         cls: 'cw',
12608                         html: calWeek
12609                     });
12610                 }
12611             }
12612             
12613             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12614                 clsName += ' old';
12615             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12616                 clsName += ' new';
12617             }
12618             if (this.todayHighlight &&
12619                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12620                 prevMonth.getUTCMonth() == today.getMonth() &&
12621                 prevMonth.getUTCDate() == today.getDate()) {
12622                 clsName += ' today';
12623             }
12624             
12625             if (currentDate && prevMonth.valueOf() === currentDate) {
12626                 clsName += ' active';
12627             }
12628             
12629             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12630                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12631                     clsName += ' disabled';
12632             }
12633             
12634             fillMonths.cn.push({
12635                 tag: 'td',
12636                 cls: 'day ' + clsName,
12637                 html: prevMonth.getDate()
12638             })
12639             
12640             prevMonth.setDate(prevMonth.getDate()+1);
12641         }
12642           
12643         var currentYear = this.date && this.date.getUTCFullYear();
12644         var currentMonth = this.date && this.date.getUTCMonth();
12645         
12646         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12647         
12648         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12649             v.removeClass('active');
12650             
12651             if(currentYear === year && k === currentMonth){
12652                 v.addClass('active');
12653             }
12654             
12655             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12656                 v.addClass('disabled');
12657             }
12658             
12659         });
12660         
12661         
12662         year = parseInt(year/10, 10) * 10;
12663         
12664         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12665         
12666         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12667         
12668         year -= 1;
12669         for (var i = -1; i < 11; i++) {
12670             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12671                 tag: 'span',
12672                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12673                 html: year
12674             })
12675             
12676             year += 1;
12677         }
12678     },
12679     
12680     showMode: function(dir) {
12681         if (dir) {
12682             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12683         }
12684         Roo.each(this.picker().select('>div',true).elements, function(v){
12685             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12686             v.hide();
12687         });
12688         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12689     },
12690     
12691     place: function()
12692     {
12693         if(this.isInline) return;
12694         
12695         this.picker().removeClass(['bottom', 'top']);
12696         
12697         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12698             /*
12699              * place to the top of element!
12700              *
12701              */
12702             
12703             this.picker().addClass('top');
12704             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12705             
12706             return;
12707         }
12708         
12709         this.picker().addClass('bottom');
12710         
12711         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12712     },
12713     
12714     parseDate : function(value){
12715         if(!value || value instanceof Date){
12716             return value;
12717         }
12718         var v = Date.parseDate(value, this.format);
12719         if (!v && this.useIso) {
12720             v = Date.parseDate(value, 'Y-m-d');
12721         }
12722         if(!v && this.altFormats){
12723             if(!this.altFormatsArray){
12724                 this.altFormatsArray = this.altFormats.split("|");
12725             }
12726             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12727                 v = Date.parseDate(value, this.altFormatsArray[i]);
12728             }
12729         }
12730         return v;
12731     },
12732     
12733     formatDate : function(date, fmt){
12734         return (!date || !(date instanceof Date)) ?
12735         date : date.dateFormat(fmt || this.format);
12736     },
12737     
12738     onFocus : function()
12739     {
12740         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12741         this.show();
12742     },
12743     
12744     onBlur : function()
12745     {
12746         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12747         this.hide();
12748     },
12749     
12750     show : function()
12751     {
12752         this.picker().show();
12753         this.update();
12754         this.place();
12755         
12756         this.fireEvent('show', this, this.date);
12757     },
12758     
12759     hide : function()
12760     {
12761         if(this.isInline) return;
12762         this.picker().hide();
12763         this.viewMode = this.startViewMode;
12764         this.showMode();
12765         
12766         this.fireEvent('hide', this, this.date);
12767         
12768     },
12769     
12770     onMousedown: function(e){
12771         e.stopPropagation();
12772         e.preventDefault();
12773     },
12774     
12775     keyup: function(e){
12776         Roo.bootstrap.DateField.superclass.keyup.call(this);
12777         this.update();
12778         
12779     },
12780
12781     setValue: function(v){
12782         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12783         
12784         this.fireEvent('select', this, this.date);
12785         
12786     },
12787     
12788     fireKey: function(e){
12789         if (!this.picker().isVisible()){
12790             if (e.keyCode == 27) // allow escape to hide and re-show picker
12791                 this.show();
12792             return;
12793         }
12794         var dateChanged = false,
12795         dir, day, month,
12796         newDate, newViewDate;
12797         switch(e.keyCode){
12798             case 27: // escape
12799                 this.hide();
12800                 e.preventDefault();
12801                 break;
12802             case 37: // left
12803             case 39: // right
12804                 if (!this.keyboardNavigation) break;
12805                 dir = e.keyCode == 37 ? -1 : 1;
12806                 
12807                 if (e.ctrlKey){
12808                     newDate = this.moveYear(this.date, dir);
12809                     newViewDate = this.moveYear(this.viewDate, dir);
12810                 } else if (e.shiftKey){
12811                     newDate = this.moveMonth(this.date, dir);
12812                     newViewDate = this.moveMonth(this.viewDate, dir);
12813                 } else {
12814                     newDate = new Date(this.date);
12815                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12816                     newViewDate = new Date(this.viewDate);
12817                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12818                 }
12819                 if (this.dateWithinRange(newDate)){
12820                     this.date = newDate;
12821                     this.viewDate = newViewDate;
12822                     this.setValue(this.formatDate(this.date));
12823                     this.update();
12824                     e.preventDefault();
12825                     dateChanged = true;
12826                 }
12827                 break;
12828             case 38: // up
12829             case 40: // down
12830                 if (!this.keyboardNavigation) break;
12831                 dir = e.keyCode == 38 ? -1 : 1;
12832                 if (e.ctrlKey){
12833                     newDate = this.moveYear(this.date, dir);
12834                     newViewDate = this.moveYear(this.viewDate, dir);
12835                 } else if (e.shiftKey){
12836                     newDate = this.moveMonth(this.date, dir);
12837                     newViewDate = this.moveMonth(this.viewDate, dir);
12838                 } else {
12839                     newDate = new Date(this.date);
12840                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12841                     newViewDate = new Date(this.viewDate);
12842                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12843                 }
12844                 if (this.dateWithinRange(newDate)){
12845                     this.date = newDate;
12846                     this.viewDate = newViewDate;
12847                     this.setValue(this.formatDate(this.date));
12848                     this.update();
12849                     e.preventDefault();
12850                     dateChanged = true;
12851                 }
12852                 break;
12853             case 13: // enter
12854                 this.setValue(this.formatDate(this.date));
12855                 this.hide();
12856                 e.preventDefault();
12857                 break;
12858             case 9: // tab
12859                 this.setValue(this.formatDate(this.date));
12860                 this.hide();
12861                 break;
12862         }
12863     },
12864     
12865     
12866     onClick: function(e) {
12867         e.stopPropagation();
12868         e.preventDefault();
12869         
12870         var target = e.getTarget();
12871         
12872         if(target.nodeName.toLowerCase() === 'i'){
12873             target = Roo.get(target).dom.parentNode;
12874         }
12875         
12876         var nodeName = target.nodeName;
12877         var className = target.className;
12878         var html = target.innerHTML;
12879         
12880         switch(nodeName.toLowerCase()) {
12881             case 'th':
12882                 switch(className) {
12883                     case 'switch':
12884                         this.showMode(1);
12885                         break;
12886                     case 'prev':
12887                     case 'next':
12888                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12889                         switch(this.viewMode){
12890                                 case 0:
12891                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12892                                         break;
12893                                 case 1:
12894                                 case 2:
12895                                         this.viewDate = this.moveYear(this.viewDate, dir);
12896                                         break;
12897                         }
12898                         this.fill();
12899                         break;
12900                     case 'today':
12901                         var date = new Date();
12902                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12903                         this.fill()
12904                         this.setValue(this.formatDate(this.date));
12905                         this.hide();
12906                         break;
12907                 }
12908                 break;
12909             case 'span':
12910                 if (className.indexOf('disabled') === -1) {
12911                     this.viewDate.setUTCDate(1);
12912                     if (className.indexOf('month') !== -1) {
12913                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12914                     } else {
12915                         var year = parseInt(html, 10) || 0;
12916                         this.viewDate.setUTCFullYear(year);
12917                         
12918                     }
12919                     this.showMode(-1);
12920                     this.fill();
12921                 }
12922                 break;
12923                 
12924             case 'td':
12925                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12926                     var day = parseInt(html, 10) || 1;
12927                     var year = this.viewDate.getUTCFullYear(),
12928                         month = this.viewDate.getUTCMonth();
12929
12930                     if (className.indexOf('old') !== -1) {
12931                         if(month === 0 ){
12932                             month = 11;
12933                             year -= 1;
12934                         }else{
12935                             month -= 1;
12936                         }
12937                     } else if (className.indexOf('new') !== -1) {
12938                         if (month == 11) {
12939                             month = 0;
12940                             year += 1;
12941                         } else {
12942                             month += 1;
12943                         }
12944                     }
12945                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12946                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12947                     this.fill();
12948                     this.setValue(this.formatDate(this.date));
12949                     this.hide();
12950                 }
12951                 break;
12952         }
12953     },
12954     
12955     setStartDate: function(startDate){
12956         this.startDate = startDate || -Infinity;
12957         if (this.startDate !== -Infinity) {
12958             this.startDate = this.parseDate(this.startDate);
12959         }
12960         this.update();
12961         this.updateNavArrows();
12962     },
12963
12964     setEndDate: function(endDate){
12965         this.endDate = endDate || Infinity;
12966         if (this.endDate !== Infinity) {
12967             this.endDate = this.parseDate(this.endDate);
12968         }
12969         this.update();
12970         this.updateNavArrows();
12971     },
12972     
12973     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12974         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12975         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12976             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12977         }
12978         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12979             return parseInt(d, 10);
12980         });
12981         this.update();
12982         this.updateNavArrows();
12983     },
12984     
12985     updateNavArrows: function() {
12986         var d = new Date(this.viewDate),
12987         year = d.getUTCFullYear(),
12988         month = d.getUTCMonth();
12989         
12990         Roo.each(this.picker().select('.prev', true).elements, function(v){
12991             v.show();
12992             switch (this.viewMode) {
12993                 case 0:
12994
12995                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12996                         v.hide();
12997                     }
12998                     break;
12999                 case 1:
13000                 case 2:
13001                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13002                         v.hide();
13003                     }
13004                     break;
13005             }
13006         });
13007         
13008         Roo.each(this.picker().select('.next', true).elements, function(v){
13009             v.show();
13010             switch (this.viewMode) {
13011                 case 0:
13012
13013                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13014                         v.hide();
13015                     }
13016                     break;
13017                 case 1:
13018                 case 2:
13019                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13020                         v.hide();
13021                     }
13022                     break;
13023             }
13024         })
13025     },
13026     
13027     moveMonth: function(date, dir){
13028         if (!dir) return date;
13029         var new_date = new Date(date.valueOf()),
13030         day = new_date.getUTCDate(),
13031         month = new_date.getUTCMonth(),
13032         mag = Math.abs(dir),
13033         new_month, test;
13034         dir = dir > 0 ? 1 : -1;
13035         if (mag == 1){
13036             test = dir == -1
13037             // If going back one month, make sure month is not current month
13038             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13039             ? function(){
13040                 return new_date.getUTCMonth() == month;
13041             }
13042             // If going forward one month, make sure month is as expected
13043             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13044             : function(){
13045                 return new_date.getUTCMonth() != new_month;
13046             };
13047             new_month = month + dir;
13048             new_date.setUTCMonth(new_month);
13049             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13050             if (new_month < 0 || new_month > 11)
13051                 new_month = (new_month + 12) % 12;
13052         } else {
13053             // For magnitudes >1, move one month at a time...
13054             for (var i=0; i<mag; i++)
13055                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13056                 new_date = this.moveMonth(new_date, dir);
13057             // ...then reset the day, keeping it in the new month
13058             new_month = new_date.getUTCMonth();
13059             new_date.setUTCDate(day);
13060             test = function(){
13061                 return new_month != new_date.getUTCMonth();
13062             };
13063         }
13064         // Common date-resetting loop -- if date is beyond end of month, make it
13065         // end of month
13066         while (test()){
13067             new_date.setUTCDate(--day);
13068             new_date.setUTCMonth(new_month);
13069         }
13070         return new_date;
13071     },
13072
13073     moveYear: function(date, dir){
13074         return this.moveMonth(date, dir*12);
13075     },
13076
13077     dateWithinRange: function(date){
13078         return date >= this.startDate && date <= this.endDate;
13079     },
13080
13081     
13082     remove: function() {
13083         this.picker().remove();
13084     }
13085    
13086 });
13087
13088 Roo.apply(Roo.bootstrap.DateField,  {
13089     
13090     head : {
13091         tag: 'thead',
13092         cn: [
13093         {
13094             tag: 'tr',
13095             cn: [
13096             {
13097                 tag: 'th',
13098                 cls: 'prev',
13099                 html: '<i class="icon-arrow-left"/>'
13100             },
13101             {
13102                 tag: 'th',
13103                 cls: 'switch',
13104                 colspan: '5'
13105             },
13106             {
13107                 tag: 'th',
13108                 cls: 'next',
13109                 html: '<i class="icon-arrow-right"/>'
13110             }
13111
13112             ]
13113         }
13114         ]
13115     },
13116     
13117     content : {
13118         tag: 'tbody',
13119         cn: [
13120         {
13121             tag: 'tr',
13122             cn: [
13123             {
13124                 tag: 'td',
13125                 colspan: '7'
13126             }
13127             ]
13128         }
13129         ]
13130     },
13131     
13132     footer : {
13133         tag: 'tfoot',
13134         cn: [
13135         {
13136             tag: 'tr',
13137             cn: [
13138             {
13139                 tag: 'th',
13140                 colspan: '7',
13141                 cls: 'today'
13142             }
13143                     
13144             ]
13145         }
13146         ]
13147     },
13148     
13149     dates:{
13150         en: {
13151             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13152             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13153             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13154             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13155             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13156             today: "Today"
13157         }
13158     },
13159     
13160     modes: [
13161     {
13162         clsName: 'days',
13163         navFnc: 'Month',
13164         navStep: 1
13165     },
13166     {
13167         clsName: 'months',
13168         navFnc: 'FullYear',
13169         navStep: 1
13170     },
13171     {
13172         clsName: 'years',
13173         navFnc: 'FullYear',
13174         navStep: 10
13175     }]
13176 });
13177
13178 Roo.apply(Roo.bootstrap.DateField,  {
13179   
13180     template : {
13181         tag: 'div',
13182         cls: 'datepicker dropdown-menu',
13183         cn: [
13184         {
13185             tag: 'div',
13186             cls: 'datepicker-days',
13187             cn: [
13188             {
13189                 tag: 'table',
13190                 cls: 'table-condensed',
13191                 cn:[
13192                 Roo.bootstrap.DateField.head,
13193                 {
13194                     tag: 'tbody'
13195                 },
13196                 Roo.bootstrap.DateField.footer
13197                 ]
13198             }
13199             ]
13200         },
13201         {
13202             tag: 'div',
13203             cls: 'datepicker-months',
13204             cn: [
13205             {
13206                 tag: 'table',
13207                 cls: 'table-condensed',
13208                 cn:[
13209                 Roo.bootstrap.DateField.head,
13210                 Roo.bootstrap.DateField.content,
13211                 Roo.bootstrap.DateField.footer
13212                 ]
13213             }
13214             ]
13215         },
13216         {
13217             tag: 'div',
13218             cls: 'datepicker-years',
13219             cn: [
13220             {
13221                 tag: 'table',
13222                 cls: 'table-condensed',
13223                 cn:[
13224                 Roo.bootstrap.DateField.head,
13225                 Roo.bootstrap.DateField.content,
13226                 Roo.bootstrap.DateField.footer
13227                 ]
13228             }
13229             ]
13230         }
13231         ]
13232     }
13233 });
13234
13235  
13236
13237  /*
13238  * - LGPL
13239  *
13240  * TimeField
13241  * 
13242  */
13243
13244 /**
13245  * @class Roo.bootstrap.TimeField
13246  * @extends Roo.bootstrap.Input
13247  * Bootstrap DateField class
13248  * 
13249  * 
13250  * @constructor
13251  * Create a new TimeField
13252  * @param {Object} config The config object
13253  */
13254
13255 Roo.bootstrap.TimeField = function(config){
13256     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13257     this.addEvents({
13258             /**
13259              * @event show
13260              * Fires when this field show.
13261              * @param {Roo.bootstrap.DateField} this
13262              * @param {Mixed} date The date value
13263              */
13264             show : true,
13265             /**
13266              * @event show
13267              * Fires when this field hide.
13268              * @param {Roo.bootstrap.DateField} this
13269              * @param {Mixed} date The date value
13270              */
13271             hide : true,
13272             /**
13273              * @event select
13274              * Fires when select a date.
13275              * @param {Roo.bootstrap.DateField} this
13276              * @param {Mixed} date The date value
13277              */
13278             select : true
13279         });
13280 };
13281
13282 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13283     
13284     /**
13285      * @cfg {String} format
13286      * The default time format string which can be overriden for localization support.  The format must be
13287      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13288      */
13289     format : "H:i",
13290        
13291     onRender: function(ct, position)
13292     {
13293         
13294         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13295                 
13296         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13297         
13298         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13299         
13300         this.pop = this.picker().select('>.datepicker-time',true).first();
13301         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13302         
13303         this.picker().on('mousedown', this.onMousedown, this);
13304         this.picker().on('click', this.onClick, this);
13305         
13306         this.picker().addClass('datepicker-dropdown');
13307     
13308         this.fillTime();
13309         this.update();
13310             
13311         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13312         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13313         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13314         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13315         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13316         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13317
13318     },
13319     
13320     fireKey: function(e){
13321         if (!this.picker().isVisible()){
13322             if (e.keyCode == 27) // allow escape to hide and re-show picker
13323                 this.show();
13324             return;
13325         }
13326
13327         e.preventDefault();
13328         
13329         switch(e.keyCode){
13330             case 27: // escape
13331                 this.hide();
13332                 break;
13333             case 37: // left
13334             case 39: // right
13335                 this.onTogglePeriod();
13336                 break;
13337             case 38: // up
13338                 this.onIncrementMinutes();
13339                 break;
13340             case 40: // down
13341                 this.onDecrementMinutes();
13342                 break;
13343             case 13: // enter
13344             case 9: // tab
13345                 this.setTime();
13346                 break;
13347         }
13348     },
13349     
13350     onClick: function(e) {
13351         e.stopPropagation();
13352         e.preventDefault();
13353     },
13354     
13355     picker : function()
13356     {
13357         return this.el.select('.datepicker', true).first();
13358     },
13359     
13360     fillTime: function()
13361     {    
13362         var time = this.pop.select('tbody', true).first();
13363         
13364         time.dom.innerHTML = '';
13365         
13366         time.createChild({
13367             tag: 'tr',
13368             cn: [
13369                 {
13370                     tag: 'td',
13371                     cn: [
13372                         {
13373                             tag: 'a',
13374                             href: '#',
13375                             cls: 'btn',
13376                             cn: [
13377                                 {
13378                                     tag: 'span',
13379                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13380                                 }
13381                             ]
13382                         } 
13383                     ]
13384                 },
13385                 {
13386                     tag: 'td',
13387                     cls: 'separator'
13388                 },
13389                 {
13390                     tag: 'td',
13391                     cn: [
13392                         {
13393                             tag: 'a',
13394                             href: '#',
13395                             cls: 'btn',
13396                             cn: [
13397                                 {
13398                                     tag: 'span',
13399                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13400                                 }
13401                             ]
13402                         }
13403                     ]
13404                 },
13405                 {
13406                     tag: 'td',
13407                     cls: 'separator'
13408                 }
13409             ]
13410         });
13411         
13412         time.createChild({
13413             tag: 'tr',
13414             cn: [
13415                 {
13416                     tag: 'td',
13417                     cn: [
13418                         {
13419                             tag: 'span',
13420                             cls: 'timepicker-hour',
13421                             html: '00'
13422                         }  
13423                     ]
13424                 },
13425                 {
13426                     tag: 'td',
13427                     cls: 'separator',
13428                     html: ':'
13429                 },
13430                 {
13431                     tag: 'td',
13432                     cn: [
13433                         {
13434                             tag: 'span',
13435                             cls: 'timepicker-minute',
13436                             html: '00'
13437                         }  
13438                     ]
13439                 },
13440                 {
13441                     tag: 'td',
13442                     cls: 'separator'
13443                 },
13444                 {
13445                     tag: 'td',
13446                     cn: [
13447                         {
13448                             tag: 'button',
13449                             type: 'button',
13450                             cls: 'btn btn-primary period',
13451                             html: 'AM'
13452                             
13453                         }
13454                     ]
13455                 }
13456             ]
13457         });
13458         
13459         time.createChild({
13460             tag: 'tr',
13461             cn: [
13462                 {
13463                     tag: 'td',
13464                     cn: [
13465                         {
13466                             tag: 'a',
13467                             href: '#',
13468                             cls: 'btn',
13469                             cn: [
13470                                 {
13471                                     tag: 'span',
13472                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13473                                 }
13474                             ]
13475                         }
13476                     ]
13477                 },
13478                 {
13479                     tag: 'td',
13480                     cls: 'separator'
13481                 },
13482                 {
13483                     tag: 'td',
13484                     cn: [
13485                         {
13486                             tag: 'a',
13487                             href: '#',
13488                             cls: 'btn',
13489                             cn: [
13490                                 {
13491                                     tag: 'span',
13492                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13493                                 }
13494                             ]
13495                         }
13496                     ]
13497                 },
13498                 {
13499                     tag: 'td',
13500                     cls: 'separator'
13501                 }
13502             ]
13503         });
13504         
13505     },
13506     
13507     update: function()
13508     {
13509         
13510         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13511         
13512         this.fill();
13513     },
13514     
13515     fill: function() 
13516     {
13517         var hours = this.time.getHours();
13518         var minutes = this.time.getMinutes();
13519         var period = 'AM';
13520         
13521         if(hours > 11){
13522             period = 'PM';
13523         }
13524         
13525         if(hours == 0){
13526             hours = 12;
13527         }
13528         
13529         
13530         if(hours > 12){
13531             hours = hours - 12;
13532         }
13533         
13534         if(hours < 10){
13535             hours = '0' + hours;
13536         }
13537         
13538         if(minutes < 10){
13539             minutes = '0' + minutes;
13540         }
13541         
13542         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13543         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13544         this.pop.select('button', true).first().dom.innerHTML = period;
13545         
13546     },
13547     
13548     place: function()
13549     {   
13550         this.picker().removeClass(['bottom', 'top']);
13551         
13552         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13553             /*
13554              * place to the top of element!
13555              *
13556              */
13557             
13558             this.picker().addClass('top');
13559             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13560             
13561             return;
13562         }
13563         
13564         this.picker().addClass('bottom');
13565         
13566         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13567     },
13568   
13569     onFocus : function()
13570     {
13571         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13572         this.show();
13573     },
13574     
13575     onBlur : function()
13576     {
13577         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13578         this.hide();
13579     },
13580     
13581     show : function()
13582     {
13583         this.picker().show();
13584         this.pop.show();
13585         this.update();
13586         this.place();
13587         
13588         this.fireEvent('show', this, this.date);
13589     },
13590     
13591     hide : function()
13592     {
13593         this.picker().hide();
13594         this.pop.hide();
13595         
13596         this.fireEvent('hide', this, this.date);
13597     },
13598     
13599     setTime : function()
13600     {
13601         this.hide();
13602         this.setValue(this.time.format(this.format));
13603         
13604         this.fireEvent('select', this, this.date);
13605         
13606         
13607     },
13608     
13609     onMousedown: function(e){
13610         e.stopPropagation();
13611         e.preventDefault();
13612     },
13613     
13614     onIncrementHours: function()
13615     {
13616         Roo.log('onIncrementHours');
13617         this.time = this.time.add(Date.HOUR, 1);
13618         this.update();
13619         
13620     },
13621     
13622     onDecrementHours: function()
13623     {
13624         Roo.log('onDecrementHours');
13625         this.time = this.time.add(Date.HOUR, -1);
13626         this.update();
13627     },
13628     
13629     onIncrementMinutes: function()
13630     {
13631         Roo.log('onIncrementMinutes');
13632         this.time = this.time.add(Date.MINUTE, 1);
13633         this.update();
13634     },
13635     
13636     onDecrementMinutes: function()
13637     {
13638         Roo.log('onDecrementMinutes');
13639         this.time = this.time.add(Date.MINUTE, -1);
13640         this.update();
13641     },
13642     
13643     onTogglePeriod: function()
13644     {
13645         Roo.log('onTogglePeriod');
13646         this.time = this.time.add(Date.HOUR, 12);
13647         this.update();
13648     }
13649     
13650    
13651 });
13652
13653 Roo.apply(Roo.bootstrap.TimeField,  {
13654     
13655     content : {
13656         tag: 'tbody',
13657         cn: [
13658             {
13659                 tag: 'tr',
13660                 cn: [
13661                 {
13662                     tag: 'td',
13663                     colspan: '7'
13664                 }
13665                 ]
13666             }
13667         ]
13668     },
13669     
13670     footer : {
13671         tag: 'tfoot',
13672         cn: [
13673             {
13674                 tag: 'tr',
13675                 cn: [
13676                 {
13677                     tag: 'th',
13678                     colspan: '7',
13679                     cls: '',
13680                     cn: [
13681                         {
13682                             tag: 'button',
13683                             cls: 'btn btn-info ok',
13684                             html: 'OK'
13685                         }
13686                     ]
13687                 }
13688
13689                 ]
13690             }
13691         ]
13692     }
13693 });
13694
13695 Roo.apply(Roo.bootstrap.TimeField,  {
13696   
13697     template : {
13698         tag: 'div',
13699         cls: 'datepicker dropdown-menu',
13700         cn: [
13701             {
13702                 tag: 'div',
13703                 cls: 'datepicker-time',
13704                 cn: [
13705                 {
13706                     tag: 'table',
13707                     cls: 'table-condensed',
13708                     cn:[
13709                     Roo.bootstrap.TimeField.content,
13710                     Roo.bootstrap.TimeField.footer
13711                     ]
13712                 }
13713                 ]
13714             }
13715         ]
13716     }
13717 });
13718
13719  
13720
13721  /*
13722  * - LGPL
13723  *
13724  * CheckBox
13725  * 
13726  */
13727
13728 /**
13729  * @class Roo.bootstrap.CheckBox
13730  * @extends Roo.bootstrap.Input
13731  * Bootstrap CheckBox class
13732  * 
13733  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13734  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13735  * @cfg {String} boxLabel The text that appears beside the checkbox
13736  * @cfg {Boolean} checked initnal the element
13737  * 
13738  * @constructor
13739  * Create a new CheckBox
13740  * @param {Object} config The config object
13741  */
13742
13743 Roo.bootstrap.CheckBox = function(config){
13744     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13745    
13746         this.addEvents({
13747             /**
13748             * @event check
13749             * Fires when the element is checked or unchecked.
13750             * @param {Roo.bootstrap.CheckBox} this This input
13751             * @param {Boolean} checked The new checked value
13752             */
13753            check : true
13754         });
13755 };
13756
13757 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13758     
13759     inputType: 'checkbox',
13760     inputValue: 1,
13761     valueOff: 0,
13762     boxLabel: false,
13763     checked: false,
13764     
13765     getAutoCreate : function()
13766     {
13767         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13768         
13769         var id = Roo.id();
13770         
13771         var cfg = {};
13772         
13773         cfg.cls = 'form-group' //input-group
13774         
13775         var input =  {
13776             tag: 'input',
13777             id : id,
13778             type : this.inputType,
13779             value : (!this.checked) ? this.valueOff : this.inputValue,
13780             cls : 'form-box',
13781             placeholder : this.placeholder || ''
13782             
13783         };
13784         
13785         if (this.disabled) {
13786             input.disabled=true;
13787         }
13788         
13789         if(this.checked){
13790             input.checked = this.checked;
13791         }
13792         
13793         if (this.name) {
13794             input.name = this.name;
13795         }
13796         
13797         if (this.size) {
13798             input.cls += ' input-' + this.size;
13799         }
13800         
13801         var settings=this;
13802         ['xs','sm','md','lg'].map(function(size){
13803             if (settings[size]) {
13804                 cfg.cls += ' col-' + size + '-' + settings[size];
13805             }
13806         });
13807         
13808         var inputblock = input;
13809         
13810         if (this.before || this.after) {
13811             
13812             inputblock = {
13813                 cls : 'input-group',
13814                 cn :  [] 
13815             };
13816             if (this.before) {
13817                 inputblock.cn.push({
13818                     tag :'span',
13819                     cls : 'input-group-addon',
13820                     html : this.before
13821                 });
13822             }
13823             inputblock.cn.push(input);
13824             if (this.after) {
13825                 inputblock.cn.push({
13826                     tag :'span',
13827                     cls : 'input-group-addon',
13828                     html : this.after
13829                 });
13830             }
13831             
13832         };
13833         
13834         if (align ==='left' && this.fieldLabel.length) {
13835                 Roo.log("left and has label");
13836                 cfg.cn = [
13837                     
13838                     {
13839                         tag: 'label',
13840                         'for' :  id,
13841                         cls : 'control-label col-md-' + this.labelWidth,
13842                         html : this.fieldLabel
13843                         
13844                     },
13845                     {
13846                         cls : "col-md-" + (12 - this.labelWidth), 
13847                         cn: [
13848                             inputblock
13849                         ]
13850                     }
13851                     
13852                 ];
13853         } else if ( this.fieldLabel.length) {
13854                 Roo.log(" label");
13855                 cfg.cn = [
13856                    
13857                     {
13858                         tag: this.boxLabel ? 'span' : 'label',
13859                         'for': id,
13860                         cls: 'control-label box-input-label',
13861                         //cls : 'input-group-addon',
13862                         html : this.fieldLabel
13863                         
13864                     },
13865                     
13866                     inputblock
13867                     
13868                 ];
13869
13870         } else {
13871             
13872                    Roo.log(" no label && no align");
13873                 cfg.cn = [
13874                     
13875                         inputblock
13876                     
13877                 ];
13878                 
13879                 
13880         };
13881         
13882         if(this.boxLabel){
13883             cfg.cn.push({
13884                 tag: 'label',
13885                 'for': id,
13886                 cls: 'box-label',
13887                 html: this.boxLabel
13888             })
13889         }
13890         
13891         return cfg;
13892         
13893     },
13894     
13895     /**
13896      * return the real input element.
13897      */
13898     inputEl: function ()
13899     {
13900         return this.el.select('input.form-box',true).first();
13901     },
13902     
13903     label: function()
13904     {
13905         return this.el.select('label.control-label',true).first();
13906     },
13907     
13908     initEvents : function()
13909     {
13910 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13911         
13912         this.inputEl().on('click', this.onClick,  this);
13913         
13914     },
13915     
13916     onClick : function()
13917     {   
13918         this.setChecked(!this.checked);
13919     },
13920     
13921     setChecked : function(state,suppressEvent)
13922     {
13923         this.checked = state;
13924         
13925         this.inputEl().dom.checked = state;
13926         
13927         if(suppressEvent !== true){
13928             this.fireEvent('check', this, state);
13929         }
13930         
13931         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13932         
13933     },
13934     
13935     setValue : function(v,suppressEvent)
13936     {
13937         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13938     }
13939     
13940 });
13941
13942  
13943 /*
13944  * - LGPL
13945  *
13946  * Radio
13947  * 
13948  */
13949
13950 /**
13951  * @class Roo.bootstrap.Radio
13952  * @extends Roo.bootstrap.CheckBox
13953  * Bootstrap Radio class
13954
13955  * @constructor
13956  * Create a new Radio
13957  * @param {Object} config The config object
13958  */
13959
13960 Roo.bootstrap.Radio = function(config){
13961     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13962    
13963 };
13964
13965 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13966     
13967     inputType: 'radio',
13968     inputValue: '',
13969     valueOff: '',
13970     
13971     getAutoCreate : function()
13972     {
13973         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13974         
13975         var id = Roo.id();
13976         
13977         var cfg = {};
13978         
13979         cfg.cls = 'form-group' //input-group
13980         
13981         var input =  {
13982             tag: 'input',
13983             id : id,
13984             type : this.inputType,
13985             value : (!this.checked) ? this.valueOff : this.inputValue,
13986             cls : 'form-box',
13987             placeholder : this.placeholder || ''
13988             
13989         };
13990         
13991         if (this.disabled) {
13992             input.disabled=true;
13993         }
13994         
13995         if(this.checked){
13996             input.checked = this.checked;
13997         }
13998         
13999         if (this.name) {
14000             input.name = this.name;
14001         }
14002         
14003         if (this.size) {
14004             input.cls += ' input-' + this.size;
14005         }
14006         
14007         var settings=this;
14008         ['xs','sm','md','lg'].map(function(size){
14009             if (settings[size]) {
14010                 cfg.cls += ' col-' + size + '-' + settings[size];
14011             }
14012         });
14013         
14014         var inputblock = input;
14015         
14016         if (this.before || this.after) {
14017             
14018             inputblock = {
14019                 cls : 'input-group',
14020                 cn :  [] 
14021             };
14022             if (this.before) {
14023                 inputblock.cn.push({
14024                     tag :'span',
14025                     cls : 'input-group-addon',
14026                     html : this.before
14027                 });
14028             }
14029             inputblock.cn.push(input);
14030             if (this.after) {
14031                 inputblock.cn.push({
14032                     tag :'span',
14033                     cls : 'input-group-addon',
14034                     html : this.after
14035                 });
14036             }
14037             
14038         };
14039         
14040         if (align ==='left' && this.fieldLabel.length) {
14041                 Roo.log("left and has label");
14042                 cfg.cn = [
14043                     
14044                     {
14045                         tag: 'label',
14046                         'for' :  id,
14047                         cls : 'control-label col-md-' + this.labelWidth,
14048                         html : this.fieldLabel
14049                         
14050                     },
14051                     {
14052                         cls : "col-md-" + (12 - this.labelWidth), 
14053                         cn: [
14054                             inputblock
14055                         ]
14056                     }
14057                     
14058                 ];
14059         } else if ( this.fieldLabel.length) {
14060                 Roo.log(" label");
14061                  cfg.cn = [
14062                    
14063                     {
14064                         tag: 'label',
14065                         'for': id,
14066                         cls: 'control-label box-input-label',
14067                         //cls : 'input-group-addon',
14068                         html : this.fieldLabel
14069                         
14070                     },
14071                     
14072                     inputblock
14073                     
14074                 ];
14075
14076         } else {
14077             
14078                    Roo.log(" no label && no align");
14079                 cfg.cn = [
14080                     
14081                         inputblock
14082                     
14083                 ];
14084                 
14085                 
14086         };
14087         
14088         if(this.boxLabel){
14089             cfg.cn.push({
14090                 tag: 'label',
14091                 'for': id,
14092                 cls: 'box-label',
14093                 html: this.boxLabel
14094             })
14095         }
14096         
14097         return cfg;
14098         
14099     },
14100    
14101     onClick : function()
14102     {   
14103         this.setChecked(true);
14104     },
14105     
14106     setChecked : function(state,suppressEvent)
14107     {
14108         if(state){
14109             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14110                 v.dom.checked = false;
14111             });
14112         }
14113         
14114         this.checked = state;
14115         this.inputEl().dom.checked = state;
14116         
14117         if(suppressEvent !== true){
14118             this.fireEvent('check', this, state);
14119         }
14120         
14121         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14122         
14123     },
14124     
14125     getGroupValue : function()
14126     {
14127         var value = ''
14128         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14129             if(v.dom.checked == true){
14130                 value = v.dom.value;
14131             }
14132         });
14133         
14134         return value;
14135     },
14136     
14137     /**
14138      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14139      * @return {Mixed} value The field value
14140      */
14141     getValue : function(){
14142         return this.getGroupValue();
14143     }
14144     
14145 });
14146
14147  
14148 //<script type="text/javascript">
14149
14150 /*
14151  * Based  Ext JS Library 1.1.1
14152  * Copyright(c) 2006-2007, Ext JS, LLC.
14153  * LGPL
14154  *
14155  */
14156  
14157 /**
14158  * @class Roo.HtmlEditorCore
14159  * @extends Roo.Component
14160  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14161  *
14162  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14163  */
14164
14165 Roo.HtmlEditorCore = function(config){
14166     
14167     
14168     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14169     this.addEvents({
14170         /**
14171          * @event initialize
14172          * Fires when the editor is fully initialized (including the iframe)
14173          * @param {Roo.HtmlEditorCore} this
14174          */
14175         initialize: true,
14176         /**
14177          * @event activate
14178          * Fires when the editor is first receives the focus. Any insertion must wait
14179          * until after this event.
14180          * @param {Roo.HtmlEditorCore} this
14181          */
14182         activate: true,
14183          /**
14184          * @event beforesync
14185          * Fires before the textarea is updated with content from the editor iframe. Return false
14186          * to cancel the sync.
14187          * @param {Roo.HtmlEditorCore} this
14188          * @param {String} html
14189          */
14190         beforesync: true,
14191          /**
14192          * @event beforepush
14193          * Fires before the iframe editor is updated with content from the textarea. Return false
14194          * to cancel the push.
14195          * @param {Roo.HtmlEditorCore} this
14196          * @param {String} html
14197          */
14198         beforepush: true,
14199          /**
14200          * @event sync
14201          * Fires when the textarea is updated with content from the editor iframe.
14202          * @param {Roo.HtmlEditorCore} this
14203          * @param {String} html
14204          */
14205         sync: true,
14206          /**
14207          * @event push
14208          * Fires when the iframe editor is updated with content from the textarea.
14209          * @param {Roo.HtmlEditorCore} this
14210          * @param {String} html
14211          */
14212         push: true,
14213         
14214         /**
14215          * @event editorevent
14216          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14217          * @param {Roo.HtmlEditorCore} this
14218          */
14219         editorevent: true
14220     });
14221      
14222 };
14223
14224
14225 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14226
14227
14228      /**
14229      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14230      */
14231     
14232     owner : false,
14233     
14234      /**
14235      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14236      *                        Roo.resizable.
14237      */
14238     resizable : false,
14239      /**
14240      * @cfg {Number} height (in pixels)
14241      */   
14242     height: 300,
14243    /**
14244      * @cfg {Number} width (in pixels)
14245      */   
14246     width: 500,
14247     
14248     /**
14249      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14250      * 
14251      */
14252     stylesheets: false,
14253     
14254     // id of frame..
14255     frameId: false,
14256     
14257     // private properties
14258     validationEvent : false,
14259     deferHeight: true,
14260     initialized : false,
14261     activated : false,
14262     sourceEditMode : false,
14263     onFocus : Roo.emptyFn,
14264     iframePad:3,
14265     hideMode:'offsets',
14266     
14267     clearUp: true,
14268     
14269      
14270     
14271
14272     /**
14273      * Protected method that will not generally be called directly. It
14274      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14275      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14276      */
14277     getDocMarkup : function(){
14278         // body styles..
14279         var st = '';
14280         Roo.log(this.stylesheets);
14281         
14282         // inherit styels from page...?? 
14283         if (this.stylesheets === false) {
14284             
14285             Roo.get(document.head).select('style').each(function(node) {
14286                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14287             });
14288             
14289             Roo.get(document.head).select('link').each(function(node) { 
14290                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14291             });
14292             
14293         } else if (!this.stylesheets.length) {
14294                 // simple..
14295                 st = '<style type="text/css">' +
14296                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14297                    '</style>';
14298         } else {
14299             Roo.each(this.stylesheets, function(s) {
14300                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14301             });
14302             
14303         }
14304         
14305         st +=  '<style type="text/css">' +
14306             'IMG { cursor: pointer } ' +
14307         '</style>';
14308
14309         
14310         return '<html><head>' + st  +
14311             //<style type="text/css">' +
14312             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14313             //'</style>' +
14314             ' </head><body class="roo-htmleditor-body"></body></html>';
14315     },
14316
14317     // private
14318     onRender : function(ct, position)
14319     {
14320         var _t = this;
14321         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14322         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14323         
14324         
14325         this.el.dom.style.border = '0 none';
14326         this.el.dom.setAttribute('tabIndex', -1);
14327         this.el.addClass('x-hidden hide');
14328         
14329         
14330         
14331         if(Roo.isIE){ // fix IE 1px bogus margin
14332             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14333         }
14334        
14335         
14336         this.frameId = Roo.id();
14337         
14338          
14339         
14340         var iframe = this.owner.wrap.createChild({
14341             tag: 'iframe',
14342             cls: 'form-control', // bootstrap..
14343             id: this.frameId,
14344             name: this.frameId,
14345             frameBorder : 'no',
14346             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14347         }, this.el
14348         );
14349         
14350         
14351         this.iframe = iframe.dom;
14352
14353          this.assignDocWin();
14354         
14355         this.doc.designMode = 'on';
14356        
14357         this.doc.open();
14358         this.doc.write(this.getDocMarkup());
14359         this.doc.close();
14360
14361         
14362         var task = { // must defer to wait for browser to be ready
14363             run : function(){
14364                 //console.log("run task?" + this.doc.readyState);
14365                 this.assignDocWin();
14366                 if(this.doc.body || this.doc.readyState == 'complete'){
14367                     try {
14368                         this.doc.designMode="on";
14369                     } catch (e) {
14370                         return;
14371                     }
14372                     Roo.TaskMgr.stop(task);
14373                     this.initEditor.defer(10, this);
14374                 }
14375             },
14376             interval : 10,
14377             duration: 10000,
14378             scope: this
14379         };
14380         Roo.TaskMgr.start(task);
14381
14382         
14383          
14384     },
14385
14386     // private
14387     onResize : function(w, h)
14388     {
14389          Roo.log('resize: ' +w + ',' + h );
14390         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14391         if(!this.iframe){
14392             return;
14393         }
14394         if(typeof w == 'number'){
14395             
14396             this.iframe.style.width = w + 'px';
14397         }
14398         if(typeof h == 'number'){
14399             
14400             this.iframe.style.height = h + 'px';
14401             if(this.doc){
14402                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14403             }
14404         }
14405         
14406     },
14407
14408     /**
14409      * Toggles the editor between standard and source edit mode.
14410      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14411      */
14412     toggleSourceEdit : function(sourceEditMode){
14413         
14414         this.sourceEditMode = sourceEditMode === true;
14415         
14416         if(this.sourceEditMode){
14417  
14418             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14419             
14420         }else{
14421             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14422             //this.iframe.className = '';
14423             this.deferFocus();
14424         }
14425         //this.setSize(this.owner.wrap.getSize());
14426         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14427     },
14428
14429     
14430   
14431
14432     /**
14433      * Protected method that will not generally be called directly. If you need/want
14434      * custom HTML cleanup, this is the method you should override.
14435      * @param {String} html The HTML to be cleaned
14436      * return {String} The cleaned HTML
14437      */
14438     cleanHtml : function(html){
14439         html = String(html);
14440         if(html.length > 5){
14441             if(Roo.isSafari){ // strip safari nonsense
14442                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14443             }
14444         }
14445         if(html == '&nbsp;'){
14446             html = '';
14447         }
14448         return html;
14449     },
14450
14451     /**
14452      * HTML Editor -> Textarea
14453      * Protected method that will not generally be called directly. Syncs the contents
14454      * of the editor iframe with the textarea.
14455      */
14456     syncValue : function(){
14457         if(this.initialized){
14458             var bd = (this.doc.body || this.doc.documentElement);
14459             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14460             var html = bd.innerHTML;
14461             if(Roo.isSafari){
14462                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14463                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14464                 if(m && m[1]){
14465                     html = '<div style="'+m[0]+'">' + html + '</div>';
14466                 }
14467             }
14468             html = this.cleanHtml(html);
14469             // fix up the special chars.. normaly like back quotes in word...
14470             // however we do not want to do this with chinese..
14471             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14472                 var cc = b.charCodeAt();
14473                 if (
14474                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14475                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14476                     (cc >= 0xf900 && cc < 0xfb00 )
14477                 ) {
14478                         return b;
14479                 }
14480                 return "&#"+cc+";" 
14481             });
14482             if(this.owner.fireEvent('beforesync', this, html) !== false){
14483                 this.el.dom.value = html;
14484                 this.owner.fireEvent('sync', this, html);
14485             }
14486         }
14487     },
14488
14489     /**
14490      * Protected method that will not generally be called directly. Pushes the value of the textarea
14491      * into the iframe editor.
14492      */
14493     pushValue : function(){
14494         if(this.initialized){
14495             var v = this.el.dom.value.trim();
14496             
14497 //            if(v.length < 1){
14498 //                v = '&#160;';
14499 //            }
14500             
14501             if(this.owner.fireEvent('beforepush', this, v) !== false){
14502                 var d = (this.doc.body || this.doc.documentElement);
14503                 d.innerHTML = v;
14504                 this.cleanUpPaste();
14505                 this.el.dom.value = d.innerHTML;
14506                 this.owner.fireEvent('push', this, v);
14507             }
14508         }
14509     },
14510
14511     // private
14512     deferFocus : function(){
14513         this.focus.defer(10, this);
14514     },
14515
14516     // doc'ed in Field
14517     focus : function(){
14518         if(this.win && !this.sourceEditMode){
14519             this.win.focus();
14520         }else{
14521             this.el.focus();
14522         }
14523     },
14524     
14525     assignDocWin: function()
14526     {
14527         var iframe = this.iframe;
14528         
14529          if(Roo.isIE){
14530             this.doc = iframe.contentWindow.document;
14531             this.win = iframe.contentWindow;
14532         } else {
14533             if (!Roo.get(this.frameId)) {
14534                 return;
14535             }
14536             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14537             this.win = Roo.get(this.frameId).dom.contentWindow;
14538         }
14539     },
14540     
14541     // private
14542     initEditor : function(){
14543         //console.log("INIT EDITOR");
14544         this.assignDocWin();
14545         
14546         
14547         
14548         this.doc.designMode="on";
14549         this.doc.open();
14550         this.doc.write(this.getDocMarkup());
14551         this.doc.close();
14552         
14553         var dbody = (this.doc.body || this.doc.documentElement);
14554         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14555         // this copies styles from the containing element into thsi one..
14556         // not sure why we need all of this..
14557         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14558         ss['background-attachment'] = 'fixed'; // w3c
14559         dbody.bgProperties = 'fixed'; // ie
14560         Roo.DomHelper.applyStyles(dbody, ss);
14561         Roo.EventManager.on(this.doc, {
14562             //'mousedown': this.onEditorEvent,
14563             'mouseup': this.onEditorEvent,
14564             'dblclick': this.onEditorEvent,
14565             'click': this.onEditorEvent,
14566             'keyup': this.onEditorEvent,
14567             buffer:100,
14568             scope: this
14569         });
14570         if(Roo.isGecko){
14571             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14572         }
14573         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14574             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14575         }
14576         this.initialized = true;
14577
14578         this.owner.fireEvent('initialize', this);
14579         this.pushValue();
14580     },
14581
14582     // private
14583     onDestroy : function(){
14584         
14585         
14586         
14587         if(this.rendered){
14588             
14589             //for (var i =0; i < this.toolbars.length;i++) {
14590             //    // fixme - ask toolbars for heights?
14591             //    this.toolbars[i].onDestroy();
14592            // }
14593             
14594             //this.wrap.dom.innerHTML = '';
14595             //this.wrap.remove();
14596         }
14597     },
14598
14599     // private
14600     onFirstFocus : function(){
14601         
14602         this.assignDocWin();
14603         
14604         
14605         this.activated = true;
14606          
14607     
14608         if(Roo.isGecko){ // prevent silly gecko errors
14609             this.win.focus();
14610             var s = this.win.getSelection();
14611             if(!s.focusNode || s.focusNode.nodeType != 3){
14612                 var r = s.getRangeAt(0);
14613                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14614                 r.collapse(true);
14615                 this.deferFocus();
14616             }
14617             try{
14618                 this.execCmd('useCSS', true);
14619                 this.execCmd('styleWithCSS', false);
14620             }catch(e){}
14621         }
14622         this.owner.fireEvent('activate', this);
14623     },
14624
14625     // private
14626     adjustFont: function(btn){
14627         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14628         //if(Roo.isSafari){ // safari
14629         //    adjust *= 2;
14630        // }
14631         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14632         if(Roo.isSafari){ // safari
14633             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14634             v =  (v < 10) ? 10 : v;
14635             v =  (v > 48) ? 48 : v;
14636             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14637             
14638         }
14639         
14640         
14641         v = Math.max(1, v+adjust);
14642         
14643         this.execCmd('FontSize', v  );
14644     },
14645
14646     onEditorEvent : function(e){
14647         this.owner.fireEvent('editorevent', this, e);
14648       //  this.updateToolbar();
14649         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14650     },
14651
14652     insertTag : function(tg)
14653     {
14654         // could be a bit smarter... -> wrap the current selected tRoo..
14655         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14656             
14657             range = this.createRange(this.getSelection());
14658             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14659             wrappingNode.appendChild(range.extractContents());
14660             range.insertNode(wrappingNode);
14661
14662             return;
14663             
14664             
14665             
14666         }
14667         this.execCmd("formatblock",   tg);
14668         
14669     },
14670     
14671     insertText : function(txt)
14672     {
14673         
14674         
14675         var range = this.createRange();
14676         range.deleteContents();
14677                //alert(Sender.getAttribute('label'));
14678                
14679         range.insertNode(this.doc.createTextNode(txt));
14680     } ,
14681     
14682      
14683
14684     /**
14685      * Executes a Midas editor command on the editor document and performs necessary focus and
14686      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14687      * @param {String} cmd The Midas command
14688      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14689      */
14690     relayCmd : function(cmd, value){
14691         this.win.focus();
14692         this.execCmd(cmd, value);
14693         this.owner.fireEvent('editorevent', this);
14694         //this.updateToolbar();
14695         this.owner.deferFocus();
14696     },
14697
14698     /**
14699      * Executes a Midas editor command directly on the editor document.
14700      * For visual commands, you should use {@link #relayCmd} instead.
14701      * <b>This should only be called after the editor is initialized.</b>
14702      * @param {String} cmd The Midas command
14703      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14704      */
14705     execCmd : function(cmd, value){
14706         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14707         this.syncValue();
14708     },
14709  
14710  
14711    
14712     /**
14713      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14714      * to insert tRoo.
14715      * @param {String} text | dom node.. 
14716      */
14717     insertAtCursor : function(text)
14718     {
14719         
14720         
14721         
14722         if(!this.activated){
14723             return;
14724         }
14725         /*
14726         if(Roo.isIE){
14727             this.win.focus();
14728             var r = this.doc.selection.createRange();
14729             if(r){
14730                 r.collapse(true);
14731                 r.pasteHTML(text);
14732                 this.syncValue();
14733                 this.deferFocus();
14734             
14735             }
14736             return;
14737         }
14738         */
14739         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14740             this.win.focus();
14741             
14742             
14743             // from jquery ui (MIT licenced)
14744             var range, node;
14745             var win = this.win;
14746             
14747             if (win.getSelection && win.getSelection().getRangeAt) {
14748                 range = win.getSelection().getRangeAt(0);
14749                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14750                 range.insertNode(node);
14751             } else if (win.document.selection && win.document.selection.createRange) {
14752                 // no firefox support
14753                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14754                 win.document.selection.createRange().pasteHTML(txt);
14755             } else {
14756                 // no firefox support
14757                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14758                 this.execCmd('InsertHTML', txt);
14759             } 
14760             
14761             this.syncValue();
14762             
14763             this.deferFocus();
14764         }
14765     },
14766  // private
14767     mozKeyPress : function(e){
14768         if(e.ctrlKey){
14769             var c = e.getCharCode(), cmd;
14770           
14771             if(c > 0){
14772                 c = String.fromCharCode(c).toLowerCase();
14773                 switch(c){
14774                     case 'b':
14775                         cmd = 'bold';
14776                         break;
14777                     case 'i':
14778                         cmd = 'italic';
14779                         break;
14780                     
14781                     case 'u':
14782                         cmd = 'underline';
14783                         break;
14784                     
14785                     case 'v':
14786                         this.cleanUpPaste.defer(100, this);
14787                         return;
14788                         
14789                 }
14790                 if(cmd){
14791                     this.win.focus();
14792                     this.execCmd(cmd);
14793                     this.deferFocus();
14794                     e.preventDefault();
14795                 }
14796                 
14797             }
14798         }
14799     },
14800
14801     // private
14802     fixKeys : function(){ // load time branching for fastest keydown performance
14803         if(Roo.isIE){
14804             return function(e){
14805                 var k = e.getKey(), r;
14806                 if(k == e.TAB){
14807                     e.stopEvent();
14808                     r = this.doc.selection.createRange();
14809                     if(r){
14810                         r.collapse(true);
14811                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14812                         this.deferFocus();
14813                     }
14814                     return;
14815                 }
14816                 
14817                 if(k == e.ENTER){
14818                     r = this.doc.selection.createRange();
14819                     if(r){
14820                         var target = r.parentElement();
14821                         if(!target || target.tagName.toLowerCase() != 'li'){
14822                             e.stopEvent();
14823                             r.pasteHTML('<br />');
14824                             r.collapse(false);
14825                             r.select();
14826                         }
14827                     }
14828                 }
14829                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14830                     this.cleanUpPaste.defer(100, this);
14831                     return;
14832                 }
14833                 
14834                 
14835             };
14836         }else if(Roo.isOpera){
14837             return function(e){
14838                 var k = e.getKey();
14839                 if(k == e.TAB){
14840                     e.stopEvent();
14841                     this.win.focus();
14842                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14843                     this.deferFocus();
14844                 }
14845                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14846                     this.cleanUpPaste.defer(100, this);
14847                     return;
14848                 }
14849                 
14850             };
14851         }else if(Roo.isSafari){
14852             return function(e){
14853                 var k = e.getKey();
14854                 
14855                 if(k == e.TAB){
14856                     e.stopEvent();
14857                     this.execCmd('InsertText','\t');
14858                     this.deferFocus();
14859                     return;
14860                 }
14861                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14862                     this.cleanUpPaste.defer(100, this);
14863                     return;
14864                 }
14865                 
14866              };
14867         }
14868     }(),
14869     
14870     getAllAncestors: function()
14871     {
14872         var p = this.getSelectedNode();
14873         var a = [];
14874         if (!p) {
14875             a.push(p); // push blank onto stack..
14876             p = this.getParentElement();
14877         }
14878         
14879         
14880         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14881             a.push(p);
14882             p = p.parentNode;
14883         }
14884         a.push(this.doc.body);
14885         return a;
14886     },
14887     lastSel : false,
14888     lastSelNode : false,
14889     
14890     
14891     getSelection : function() 
14892     {
14893         this.assignDocWin();
14894         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14895     },
14896     
14897     getSelectedNode: function() 
14898     {
14899         // this may only work on Gecko!!!
14900         
14901         // should we cache this!!!!
14902         
14903         
14904         
14905          
14906         var range = this.createRange(this.getSelection()).cloneRange();
14907         
14908         if (Roo.isIE) {
14909             var parent = range.parentElement();
14910             while (true) {
14911                 var testRange = range.duplicate();
14912                 testRange.moveToElementText(parent);
14913                 if (testRange.inRange(range)) {
14914                     break;
14915                 }
14916                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14917                     break;
14918                 }
14919                 parent = parent.parentElement;
14920             }
14921             return parent;
14922         }
14923         
14924         // is ancestor a text element.
14925         var ac =  range.commonAncestorContainer;
14926         if (ac.nodeType == 3) {
14927             ac = ac.parentNode;
14928         }
14929         
14930         var ar = ac.childNodes;
14931          
14932         var nodes = [];
14933         var other_nodes = [];
14934         var has_other_nodes = false;
14935         for (var i=0;i<ar.length;i++) {
14936             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14937                 continue;
14938             }
14939             // fullly contained node.
14940             
14941             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14942                 nodes.push(ar[i]);
14943                 continue;
14944             }
14945             
14946             // probably selected..
14947             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14948                 other_nodes.push(ar[i]);
14949                 continue;
14950             }
14951             // outer..
14952             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14953                 continue;
14954             }
14955             
14956             
14957             has_other_nodes = true;
14958         }
14959         if (!nodes.length && other_nodes.length) {
14960             nodes= other_nodes;
14961         }
14962         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14963             return false;
14964         }
14965         
14966         return nodes[0];
14967     },
14968     createRange: function(sel)
14969     {
14970         // this has strange effects when using with 
14971         // top toolbar - not sure if it's a great idea.
14972         //this.editor.contentWindow.focus();
14973         if (typeof sel != "undefined") {
14974             try {
14975                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14976             } catch(e) {
14977                 return this.doc.createRange();
14978             }
14979         } else {
14980             return this.doc.createRange();
14981         }
14982     },
14983     getParentElement: function()
14984     {
14985         
14986         this.assignDocWin();
14987         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14988         
14989         var range = this.createRange(sel);
14990          
14991         try {
14992             var p = range.commonAncestorContainer;
14993             while (p.nodeType == 3) { // text node
14994                 p = p.parentNode;
14995             }
14996             return p;
14997         } catch (e) {
14998             return null;
14999         }
15000     
15001     },
15002     /***
15003      *
15004      * Range intersection.. the hard stuff...
15005      *  '-1' = before
15006      *  '0' = hits..
15007      *  '1' = after.
15008      *         [ -- selected range --- ]
15009      *   [fail]                        [fail]
15010      *
15011      *    basically..
15012      *      if end is before start or  hits it. fail.
15013      *      if start is after end or hits it fail.
15014      *
15015      *   if either hits (but other is outside. - then it's not 
15016      *   
15017      *    
15018      **/
15019     
15020     
15021     // @see http://www.thismuchiknow.co.uk/?p=64.
15022     rangeIntersectsNode : function(range, node)
15023     {
15024         var nodeRange = node.ownerDocument.createRange();
15025         try {
15026             nodeRange.selectNode(node);
15027         } catch (e) {
15028             nodeRange.selectNodeContents(node);
15029         }
15030     
15031         var rangeStartRange = range.cloneRange();
15032         rangeStartRange.collapse(true);
15033     
15034         var rangeEndRange = range.cloneRange();
15035         rangeEndRange.collapse(false);
15036     
15037         var nodeStartRange = nodeRange.cloneRange();
15038         nodeStartRange.collapse(true);
15039     
15040         var nodeEndRange = nodeRange.cloneRange();
15041         nodeEndRange.collapse(false);
15042     
15043         return rangeStartRange.compareBoundaryPoints(
15044                  Range.START_TO_START, nodeEndRange) == -1 &&
15045                rangeEndRange.compareBoundaryPoints(
15046                  Range.START_TO_START, nodeStartRange) == 1;
15047         
15048          
15049     },
15050     rangeCompareNode : function(range, node)
15051     {
15052         var nodeRange = node.ownerDocument.createRange();
15053         try {
15054             nodeRange.selectNode(node);
15055         } catch (e) {
15056             nodeRange.selectNodeContents(node);
15057         }
15058         
15059         
15060         range.collapse(true);
15061     
15062         nodeRange.collapse(true);
15063      
15064         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15065         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15066          
15067         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15068         
15069         var nodeIsBefore   =  ss == 1;
15070         var nodeIsAfter    = ee == -1;
15071         
15072         if (nodeIsBefore && nodeIsAfter)
15073             return 0; // outer
15074         if (!nodeIsBefore && nodeIsAfter)
15075             return 1; //right trailed.
15076         
15077         if (nodeIsBefore && !nodeIsAfter)
15078             return 2;  // left trailed.
15079         // fully contined.
15080         return 3;
15081     },
15082
15083     // private? - in a new class?
15084     cleanUpPaste :  function()
15085     {
15086         // cleans up the whole document..
15087         Roo.log('cleanuppaste');
15088         
15089         this.cleanUpChildren(this.doc.body);
15090         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15091         if (clean != this.doc.body.innerHTML) {
15092             this.doc.body.innerHTML = clean;
15093         }
15094         
15095     },
15096     
15097     cleanWordChars : function(input) {// change the chars to hex code
15098         var he = Roo.HtmlEditorCore;
15099         
15100         var output = input;
15101         Roo.each(he.swapCodes, function(sw) { 
15102             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15103             
15104             output = output.replace(swapper, sw[1]);
15105         });
15106         
15107         return output;
15108     },
15109     
15110     
15111     cleanUpChildren : function (n)
15112     {
15113         if (!n.childNodes.length) {
15114             return;
15115         }
15116         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15117            this.cleanUpChild(n.childNodes[i]);
15118         }
15119     },
15120     
15121     
15122         
15123     
15124     cleanUpChild : function (node)
15125     {
15126         var ed = this;
15127         //console.log(node);
15128         if (node.nodeName == "#text") {
15129             // clean up silly Windows -- stuff?
15130             return; 
15131         }
15132         if (node.nodeName == "#comment") {
15133             node.parentNode.removeChild(node);
15134             // clean up silly Windows -- stuff?
15135             return; 
15136         }
15137         
15138         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15139             // remove node.
15140             node.parentNode.removeChild(node);
15141             return;
15142             
15143         }
15144         
15145         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15146         
15147         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15148         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15149         
15150         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15151         //    remove_keep_children = true;
15152         //}
15153         
15154         if (remove_keep_children) {
15155             this.cleanUpChildren(node);
15156             // inserts everything just before this node...
15157             while (node.childNodes.length) {
15158                 var cn = node.childNodes[0];
15159                 node.removeChild(cn);
15160                 node.parentNode.insertBefore(cn, node);
15161             }
15162             node.parentNode.removeChild(node);
15163             return;
15164         }
15165         
15166         if (!node.attributes || !node.attributes.length) {
15167             this.cleanUpChildren(node);
15168             return;
15169         }
15170         
15171         function cleanAttr(n,v)
15172         {
15173             
15174             if (v.match(/^\./) || v.match(/^\//)) {
15175                 return;
15176             }
15177             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15178                 return;
15179             }
15180             if (v.match(/^#/)) {
15181                 return;
15182             }
15183 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15184             node.removeAttribute(n);
15185             
15186         }
15187         
15188         function cleanStyle(n,v)
15189         {
15190             if (v.match(/expression/)) { //XSS?? should we even bother..
15191                 node.removeAttribute(n);
15192                 return;
15193             }
15194             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15195             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15196             
15197             
15198             var parts = v.split(/;/);
15199             var clean = [];
15200             
15201             Roo.each(parts, function(p) {
15202                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15203                 if (!p.length) {
15204                     return true;
15205                 }
15206                 var l = p.split(':').shift().replace(/\s+/g,'');
15207                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15208                 
15209                 if ( cblack.indexOf(l) > -1) {
15210 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15211                     //node.removeAttribute(n);
15212                     return true;
15213                 }
15214                 //Roo.log()
15215                 // only allow 'c whitelisted system attributes'
15216                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15217 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15218                     //node.removeAttribute(n);
15219                     return true;
15220                 }
15221                 
15222                 
15223                  
15224                 
15225                 clean.push(p);
15226                 return true;
15227             });
15228             if (clean.length) { 
15229                 node.setAttribute(n, clean.join(';'));
15230             } else {
15231                 node.removeAttribute(n);
15232             }
15233             
15234         }
15235         
15236         
15237         for (var i = node.attributes.length-1; i > -1 ; i--) {
15238             var a = node.attributes[i];
15239             //console.log(a);
15240             
15241             if (a.name.toLowerCase().substr(0,2)=='on')  {
15242                 node.removeAttribute(a.name);
15243                 continue;
15244             }
15245             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15246                 node.removeAttribute(a.name);
15247                 continue;
15248             }
15249             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15250                 cleanAttr(a.name,a.value); // fixme..
15251                 continue;
15252             }
15253             if (a.name == 'style') {
15254                 cleanStyle(a.name,a.value);
15255                 continue;
15256             }
15257             /// clean up MS crap..
15258             // tecnically this should be a list of valid class'es..
15259             
15260             
15261             if (a.name == 'class') {
15262                 if (a.value.match(/^Mso/)) {
15263                     node.className = '';
15264                 }
15265                 
15266                 if (a.value.match(/body/)) {
15267                     node.className = '';
15268                 }
15269                 continue;
15270             }
15271             
15272             // style cleanup!?
15273             // class cleanup?
15274             
15275         }
15276         
15277         
15278         this.cleanUpChildren(node);
15279         
15280         
15281     }
15282     
15283     
15284     // hide stuff that is not compatible
15285     /**
15286      * @event blur
15287      * @hide
15288      */
15289     /**
15290      * @event change
15291      * @hide
15292      */
15293     /**
15294      * @event focus
15295      * @hide
15296      */
15297     /**
15298      * @event specialkey
15299      * @hide
15300      */
15301     /**
15302      * @cfg {String} fieldClass @hide
15303      */
15304     /**
15305      * @cfg {String} focusClass @hide
15306      */
15307     /**
15308      * @cfg {String} autoCreate @hide
15309      */
15310     /**
15311      * @cfg {String} inputType @hide
15312      */
15313     /**
15314      * @cfg {String} invalidClass @hide
15315      */
15316     /**
15317      * @cfg {String} invalidText @hide
15318      */
15319     /**
15320      * @cfg {String} msgFx @hide
15321      */
15322     /**
15323      * @cfg {String} validateOnBlur @hide
15324      */
15325 });
15326
15327 Roo.HtmlEditorCore.white = [
15328         'area', 'br', 'img', 'input', 'hr', 'wbr',
15329         
15330        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15331        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15332        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15333        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15334        'table',   'ul',         'xmp', 
15335        
15336        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15337       'thead',   'tr', 
15338      
15339       'dir', 'menu', 'ol', 'ul', 'dl',
15340        
15341       'embed',  'object'
15342 ];
15343
15344
15345 Roo.HtmlEditorCore.black = [
15346     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15347         'applet', // 
15348         'base',   'basefont', 'bgsound', 'blink',  'body', 
15349         'frame',  'frameset', 'head',    'html',   'ilayer', 
15350         'iframe', 'layer',  'link',     'meta',    'object',   
15351         'script', 'style' ,'title',  'xml' // clean later..
15352 ];
15353 Roo.HtmlEditorCore.clean = [
15354     'script', 'style', 'title', 'xml'
15355 ];
15356 Roo.HtmlEditorCore.remove = [
15357     'font'
15358 ];
15359 // attributes..
15360
15361 Roo.HtmlEditorCore.ablack = [
15362     'on'
15363 ];
15364     
15365 Roo.HtmlEditorCore.aclean = [ 
15366     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15367 ];
15368
15369 // protocols..
15370 Roo.HtmlEditorCore.pwhite= [
15371         'http',  'https',  'mailto'
15372 ];
15373
15374 // white listed style attributes.
15375 Roo.HtmlEditorCore.cwhite= [
15376       //  'text-align', /// default is to allow most things..
15377       
15378          
15379 //        'font-size'//??
15380 ];
15381
15382 // black listed style attributes.
15383 Roo.HtmlEditorCore.cblack= [
15384       //  'font-size' -- this can be set by the project 
15385 ];
15386
15387
15388 Roo.HtmlEditorCore.swapCodes   =[ 
15389     [    8211, "--" ], 
15390     [    8212, "--" ], 
15391     [    8216,  "'" ],  
15392     [    8217, "'" ],  
15393     [    8220, '"' ],  
15394     [    8221, '"' ],  
15395     [    8226, "*" ],  
15396     [    8230, "..." ]
15397 ]; 
15398
15399     /*
15400  * - LGPL
15401  *
15402  * HtmlEditor
15403  * 
15404  */
15405
15406 /**
15407  * @class Roo.bootstrap.HtmlEditor
15408  * @extends Roo.bootstrap.TextArea
15409  * Bootstrap HtmlEditor class
15410
15411  * @constructor
15412  * Create a new HtmlEditor
15413  * @param {Object} config The config object
15414  */
15415
15416 Roo.bootstrap.HtmlEditor = function(config){
15417     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15418     if (!this.toolbars) {
15419         this.toolbars = [];
15420     }
15421     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15422     this.addEvents({
15423             /**
15424              * @event initialize
15425              * Fires when the editor is fully initialized (including the iframe)
15426              * @param {HtmlEditor} this
15427              */
15428             initialize: true,
15429             /**
15430              * @event activate
15431              * Fires when the editor is first receives the focus. Any insertion must wait
15432              * until after this event.
15433              * @param {HtmlEditor} this
15434              */
15435             activate: true,
15436              /**
15437              * @event beforesync
15438              * Fires before the textarea is updated with content from the editor iframe. Return false
15439              * to cancel the sync.
15440              * @param {HtmlEditor} this
15441              * @param {String} html
15442              */
15443             beforesync: true,
15444              /**
15445              * @event beforepush
15446              * Fires before the iframe editor is updated with content from the textarea. Return false
15447              * to cancel the push.
15448              * @param {HtmlEditor} this
15449              * @param {String} html
15450              */
15451             beforepush: true,
15452              /**
15453              * @event sync
15454              * Fires when the textarea is updated with content from the editor iframe.
15455              * @param {HtmlEditor} this
15456              * @param {String} html
15457              */
15458             sync: true,
15459              /**
15460              * @event push
15461              * Fires when the iframe editor is updated with content from the textarea.
15462              * @param {HtmlEditor} this
15463              * @param {String} html
15464              */
15465             push: true,
15466              /**
15467              * @event editmodechange
15468              * Fires when the editor switches edit modes
15469              * @param {HtmlEditor} this
15470              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15471              */
15472             editmodechange: true,
15473             /**
15474              * @event editorevent
15475              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15476              * @param {HtmlEditor} this
15477              */
15478             editorevent: true,
15479             /**
15480              * @event firstfocus
15481              * Fires when on first focus - needed by toolbars..
15482              * @param {HtmlEditor} this
15483              */
15484             firstfocus: true,
15485             /**
15486              * @event autosave
15487              * Auto save the htmlEditor value as a file into Events
15488              * @param {HtmlEditor} this
15489              */
15490             autosave: true,
15491             /**
15492              * @event savedpreview
15493              * preview the saved version of htmlEditor
15494              * @param {HtmlEditor} this
15495              */
15496             savedpreview: true
15497         });
15498 };
15499
15500
15501 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15502     
15503     
15504       /**
15505      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15506      */
15507     toolbars : false,
15508    
15509      /**
15510      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15511      *                        Roo.resizable.
15512      */
15513     resizable : false,
15514      /**
15515      * @cfg {Number} height (in pixels)
15516      */   
15517     height: 300,
15518    /**
15519      * @cfg {Number} width (in pixels)
15520      */   
15521     width: false,
15522     
15523     /**
15524      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15525      * 
15526      */
15527     stylesheets: false,
15528     
15529     // id of frame..
15530     frameId: false,
15531     
15532     // private properties
15533     validationEvent : false,
15534     deferHeight: true,
15535     initialized : false,
15536     activated : false,
15537     
15538     onFocus : Roo.emptyFn,
15539     iframePad:3,
15540     hideMode:'offsets',
15541     
15542     
15543     tbContainer : false,
15544     
15545     toolbarContainer :function() {
15546         return this.wrap.select('.x-html-editor-tb',true).first();
15547     },
15548
15549     /**
15550      * Protected method that will not generally be called directly. It
15551      * is called when the editor creates its toolbar. Override this method if you need to
15552      * add custom toolbar buttons.
15553      * @param {HtmlEditor} editor
15554      */
15555     createToolbar : function(){
15556         
15557         Roo.log("create toolbars");
15558         
15559         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15560         this.toolbars[0].render(this.toolbarContainer());
15561         
15562         return;
15563         
15564 //        if (!editor.toolbars || !editor.toolbars.length) {
15565 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15566 //        }
15567 //        
15568 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15569 //            editor.toolbars[i] = Roo.factory(
15570 //                    typeof(editor.toolbars[i]) == 'string' ?
15571 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15572 //                Roo.bootstrap.HtmlEditor);
15573 //            editor.toolbars[i].init(editor);
15574 //        }
15575     },
15576
15577      
15578     // private
15579     onRender : function(ct, position)
15580     {
15581        // Roo.log("Call onRender: " + this.xtype);
15582         var _t = this;
15583         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15584       
15585         this.wrap = this.inputEl().wrap({
15586             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15587         });
15588         
15589         this.editorcore.onRender(ct, position);
15590          
15591         if (this.resizable) {
15592             this.resizeEl = new Roo.Resizable(this.wrap, {
15593                 pinned : true,
15594                 wrap: true,
15595                 dynamic : true,
15596                 minHeight : this.height,
15597                 height: this.height,
15598                 handles : this.resizable,
15599                 width: this.width,
15600                 listeners : {
15601                     resize : function(r, w, h) {
15602                         _t.onResize(w,h); // -something
15603                     }
15604                 }
15605             });
15606             
15607         }
15608         this.createToolbar(this);
15609        
15610         
15611         if(!this.width && this.resizable){
15612             this.setSize(this.wrap.getSize());
15613         }
15614         if (this.resizeEl) {
15615             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15616             // should trigger onReize..
15617         }
15618         
15619     },
15620
15621     // private
15622     onResize : function(w, h)
15623     {
15624         Roo.log('resize: ' +w + ',' + h );
15625         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15626         var ew = false;
15627         var eh = false;
15628         
15629         if(this.inputEl() ){
15630             if(typeof w == 'number'){
15631                 var aw = w - this.wrap.getFrameWidth('lr');
15632                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15633                 ew = aw;
15634             }
15635             if(typeof h == 'number'){
15636                  var tbh = -11;  // fixme it needs to tool bar size!
15637                 for (var i =0; i < this.toolbars.length;i++) {
15638                     // fixme - ask toolbars for heights?
15639                     tbh += this.toolbars[i].el.getHeight();
15640                     //if (this.toolbars[i].footer) {
15641                     //    tbh += this.toolbars[i].footer.el.getHeight();
15642                     //}
15643                 }
15644               
15645                 
15646                 
15647                 
15648                 
15649                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15650                 ah -= 5; // knock a few pixes off for look..
15651                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15652                 var eh = ah;
15653             }
15654         }
15655         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15656         this.editorcore.onResize(ew,eh);
15657         
15658     },
15659
15660     /**
15661      * Toggles the editor between standard and source edit mode.
15662      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15663      */
15664     toggleSourceEdit : function(sourceEditMode)
15665     {
15666         this.editorcore.toggleSourceEdit(sourceEditMode);
15667         
15668         if(this.editorcore.sourceEditMode){
15669             Roo.log('editor - showing textarea');
15670             
15671 //            Roo.log('in');
15672 //            Roo.log(this.syncValue());
15673             this.syncValue();
15674             this.inputEl().removeClass('hide');
15675             this.inputEl().dom.removeAttribute('tabIndex');
15676             this.inputEl().focus();
15677         }else{
15678             Roo.log('editor - hiding textarea');
15679 //            Roo.log('out')
15680 //            Roo.log(this.pushValue()); 
15681             this.pushValue();
15682             
15683             this.inputEl().addClass('hide');
15684             this.inputEl().dom.setAttribute('tabIndex', -1);
15685             //this.deferFocus();
15686         }
15687          
15688         if(this.resizable){
15689             this.setSize(this.wrap.getSize());
15690         }
15691         
15692         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15693     },
15694  
15695     // private (for BoxComponent)
15696     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15697
15698     // private (for BoxComponent)
15699     getResizeEl : function(){
15700         return this.wrap;
15701     },
15702
15703     // private (for BoxComponent)
15704     getPositionEl : function(){
15705         return this.wrap;
15706     },
15707
15708     // private
15709     initEvents : function(){
15710         this.originalValue = this.getValue();
15711     },
15712
15713 //    /**
15714 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15715 //     * @method
15716 //     */
15717 //    markInvalid : Roo.emptyFn,
15718 //    /**
15719 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15720 //     * @method
15721 //     */
15722 //    clearInvalid : Roo.emptyFn,
15723
15724     setValue : function(v){
15725         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15726         this.editorcore.pushValue();
15727     },
15728
15729      
15730     // private
15731     deferFocus : function(){
15732         this.focus.defer(10, this);
15733     },
15734
15735     // doc'ed in Field
15736     focus : function(){
15737         this.editorcore.focus();
15738         
15739     },
15740       
15741
15742     // private
15743     onDestroy : function(){
15744         
15745         
15746         
15747         if(this.rendered){
15748             
15749             for (var i =0; i < this.toolbars.length;i++) {
15750                 // fixme - ask toolbars for heights?
15751                 this.toolbars[i].onDestroy();
15752             }
15753             
15754             this.wrap.dom.innerHTML = '';
15755             this.wrap.remove();
15756         }
15757     },
15758
15759     // private
15760     onFirstFocus : function(){
15761         //Roo.log("onFirstFocus");
15762         this.editorcore.onFirstFocus();
15763          for (var i =0; i < this.toolbars.length;i++) {
15764             this.toolbars[i].onFirstFocus();
15765         }
15766         
15767     },
15768     
15769     // private
15770     syncValue : function()
15771     {   
15772         this.editorcore.syncValue();
15773     },
15774     
15775     pushValue : function()
15776     {   
15777         this.editorcore.pushValue();
15778     }
15779      
15780     
15781     // hide stuff that is not compatible
15782     /**
15783      * @event blur
15784      * @hide
15785      */
15786     /**
15787      * @event change
15788      * @hide
15789      */
15790     /**
15791      * @event focus
15792      * @hide
15793      */
15794     /**
15795      * @event specialkey
15796      * @hide
15797      */
15798     /**
15799      * @cfg {String} fieldClass @hide
15800      */
15801     /**
15802      * @cfg {String} focusClass @hide
15803      */
15804     /**
15805      * @cfg {String} autoCreate @hide
15806      */
15807     /**
15808      * @cfg {String} inputType @hide
15809      */
15810     /**
15811      * @cfg {String} invalidClass @hide
15812      */
15813     /**
15814      * @cfg {String} invalidText @hide
15815      */
15816     /**
15817      * @cfg {String} msgFx @hide
15818      */
15819     /**
15820      * @cfg {String} validateOnBlur @hide
15821      */
15822 });
15823  
15824     
15825    
15826    
15827    
15828       
15829
15830 /**
15831  * @class Roo.bootstrap.HtmlEditorToolbar1
15832  * Basic Toolbar
15833  * 
15834  * Usage:
15835  *
15836  new Roo.bootstrap.HtmlEditor({
15837     ....
15838     toolbars : [
15839         new Roo.bootstrap.HtmlEditorToolbar1({
15840             disable : { fonts: 1 , format: 1, ..., ... , ...],
15841             btns : [ .... ]
15842         })
15843     }
15844      
15845  * 
15846  * @cfg {Object} disable List of elements to disable..
15847  * @cfg {Array} btns List of additional buttons.
15848  * 
15849  * 
15850  * NEEDS Extra CSS? 
15851  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15852  */
15853  
15854 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15855 {
15856     
15857     Roo.apply(this, config);
15858     
15859     // default disabled, based on 'good practice'..
15860     this.disable = this.disable || {};
15861     Roo.applyIf(this.disable, {
15862         fontSize : true,
15863         colors : true,
15864         specialElements : true
15865     });
15866     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15867     
15868     this.editor = config.editor;
15869     this.editorcore = config.editor.editorcore;
15870     
15871     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15872     
15873     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15874     // dont call parent... till later.
15875 }
15876 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
15877     
15878     
15879     bar : true,
15880     
15881     editor : false,
15882     editorcore : false,
15883     
15884     
15885     formats : [
15886         "p" ,  
15887         "h1","h2","h3","h4","h5","h6", 
15888         "pre", "code", 
15889         "abbr", "acronym", "address", "cite", "samp", "var",
15890         'div','span'
15891     ],
15892     
15893     onRender : function(ct, position)
15894     {
15895        // Roo.log("Call onRender: " + this.xtype);
15896         
15897        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15898        Roo.log(this.el);
15899        this.el.dom.style.marginBottom = '0';
15900        var _this = this;
15901        var editorcore = this.editorcore;
15902        var editor= this.editor;
15903        
15904        var children = [];
15905        var btn = function(id,cmd , toggle, handler){
15906        
15907             var  event = toggle ? 'toggle' : 'click';
15908        
15909             var a = {
15910                 size : 'sm',
15911                 xtype: 'Button',
15912                 xns: Roo.bootstrap,
15913                 glyphicon : id,
15914                 cmd : id || cmd,
15915                 enableToggle:toggle !== false,
15916                 //html : 'submit'
15917                 pressed : toggle ? false : null,
15918                 listeners : {}
15919             }
15920             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15921                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15922             }
15923             children.push(a);
15924             return a;
15925        }
15926         
15927         var style = {
15928                 xtype: 'Button',
15929                 size : 'sm',
15930                 xns: Roo.bootstrap,
15931                 glyphicon : 'font',
15932                 //html : 'submit'
15933                 menu : {
15934                     xtype: 'Menu',
15935                     xns: Roo.bootstrap,
15936                     items:  []
15937                 }
15938         };
15939         Roo.each(this.formats, function(f) {
15940             style.menu.items.push({
15941                 xtype :'MenuItem',
15942                 xns: Roo.bootstrap,
15943                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15944                 tagname : f,
15945                 listeners : {
15946                     click : function()
15947                     {
15948                         editorcore.insertTag(this.tagname);
15949                         editor.focus();
15950                     }
15951                 }
15952                 
15953             });
15954         });
15955          children.push(style);   
15956             
15957             
15958         btn('bold',false,true);
15959         btn('italic',false,true);
15960         btn('align-left', 'justifyleft',true);
15961         btn('align-center', 'justifycenter',true);
15962         btn('align-right' , 'justifyright',true);
15963         btn('link', false, false, function(btn) {
15964             //Roo.log("create link?");
15965             var url = prompt(this.createLinkText, this.defaultLinkValue);
15966             if(url && url != 'http:/'+'/'){
15967                 this.editorcore.relayCmd('createlink', url);
15968             }
15969         }),
15970         btn('list','insertunorderedlist',true);
15971         btn('pencil', false,true, function(btn){
15972                 Roo.log(this);
15973                 
15974                 this.toggleSourceEdit(btn.pressed);
15975         });
15976         /*
15977         var cog = {
15978                 xtype: 'Button',
15979                 size : 'sm',
15980                 xns: Roo.bootstrap,
15981                 glyphicon : 'cog',
15982                 //html : 'submit'
15983                 menu : {
15984                     xtype: 'Menu',
15985                     xns: Roo.bootstrap,
15986                     items:  []
15987                 }
15988         };
15989         
15990         cog.menu.items.push({
15991             xtype :'MenuItem',
15992             xns: Roo.bootstrap,
15993             html : Clean styles,
15994             tagname : f,
15995             listeners : {
15996                 click : function()
15997                 {
15998                     editorcore.insertTag(this.tagname);
15999                     editor.focus();
16000                 }
16001             }
16002             
16003         });
16004        */
16005         
16006          
16007        this.xtype = 'NavSimplebar';
16008         
16009         for(var i=0;i< children.length;i++) {
16010             
16011             this.buttons.add(this.addxtypeChild(children[i]));
16012             
16013         }
16014         
16015         editor.on('editorevent', this.updateToolbar, this);
16016     },
16017     onBtnClick : function(id)
16018     {
16019        this.editorcore.relayCmd(id);
16020        this.editorcore.focus();
16021     },
16022     
16023     /**
16024      * Protected method that will not generally be called directly. It triggers
16025      * a toolbar update by reading the markup state of the current selection in the editor.
16026      */
16027     updateToolbar: function(){
16028
16029         if(!this.editorcore.activated){
16030             this.editor.onFirstFocus(); // is this neeed?
16031             return;
16032         }
16033
16034         var btns = this.buttons; 
16035         var doc = this.editorcore.doc;
16036         btns.get('bold').setActive(doc.queryCommandState('bold'));
16037         btns.get('italic').setActive(doc.queryCommandState('italic'));
16038         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16039         
16040         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16041         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16042         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16043         
16044         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16045         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16046          /*
16047         
16048         var ans = this.editorcore.getAllAncestors();
16049         if (this.formatCombo) {
16050             
16051             
16052             var store = this.formatCombo.store;
16053             this.formatCombo.setValue("");
16054             for (var i =0; i < ans.length;i++) {
16055                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16056                     // select it..
16057                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16058                     break;
16059                 }
16060             }
16061         }
16062         
16063         
16064         
16065         // hides menus... - so this cant be on a menu...
16066         Roo.bootstrap.MenuMgr.hideAll();
16067         */
16068         Roo.bootstrap.MenuMgr.hideAll();
16069         //this.editorsyncValue();
16070     },
16071     onFirstFocus: function() {
16072         this.buttons.each(function(item){
16073            item.enable();
16074         });
16075     },
16076     toggleSourceEdit : function(sourceEditMode){
16077         
16078           
16079         if(sourceEditMode){
16080             Roo.log("disabling buttons");
16081            this.buttons.each( function(item){
16082                 if(item.cmd != 'pencil'){
16083                     item.disable();
16084                 }
16085             });
16086           
16087         }else{
16088             Roo.log("enabling buttons");
16089             if(this.editorcore.initialized){
16090                 this.buttons.each( function(item){
16091                     item.enable();
16092                 });
16093             }
16094             
16095         }
16096         Roo.log("calling toggole on editor");
16097         // tell the editor that it's been pressed..
16098         this.editor.toggleSourceEdit(sourceEditMode);
16099        
16100     }
16101 });
16102
16103
16104
16105
16106
16107 /**
16108  * @class Roo.bootstrap.Table.AbstractSelectionModel
16109  * @extends Roo.util.Observable
16110  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16111  * implemented by descendant classes.  This class should not be directly instantiated.
16112  * @constructor
16113  */
16114 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16115     this.locked = false;
16116     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16117 };
16118
16119
16120 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16121     /** @ignore Called by the grid automatically. Do not call directly. */
16122     init : function(grid){
16123         this.grid = grid;
16124         this.initEvents();
16125     },
16126
16127     /**
16128      * Locks the selections.
16129      */
16130     lock : function(){
16131         this.locked = true;
16132     },
16133
16134     /**
16135      * Unlocks the selections.
16136      */
16137     unlock : function(){
16138         this.locked = false;
16139     },
16140
16141     /**
16142      * Returns true if the selections are locked.
16143      * @return {Boolean}
16144      */
16145     isLocked : function(){
16146         return this.locked;
16147     }
16148 });
16149 /**
16150  * @class Roo.bootstrap.Table.ColumnModel
16151  * @extends Roo.util.Observable
16152  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16153  * the columns in the table.
16154  
16155  * @constructor
16156  * @param {Object} config An Array of column config objects. See this class's
16157  * config objects for details.
16158 */
16159 Roo.bootstrap.Table.ColumnModel = function(config){
16160         /**
16161      * The config passed into the constructor
16162      */
16163     this.config = config;
16164     this.lookup = {};
16165
16166     // if no id, create one
16167     // if the column does not have a dataIndex mapping,
16168     // map it to the order it is in the config
16169     for(var i = 0, len = config.length; i < len; i++){
16170         var c = config[i];
16171         if(typeof c.dataIndex == "undefined"){
16172             c.dataIndex = i;
16173         }
16174         if(typeof c.renderer == "string"){
16175             c.renderer = Roo.util.Format[c.renderer];
16176         }
16177         if(typeof c.id == "undefined"){
16178             c.id = Roo.id();
16179         }
16180 //        if(c.editor && c.editor.xtype){
16181 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16182 //        }
16183 //        if(c.editor && c.editor.isFormField){
16184 //            c.editor = new Roo.grid.GridEditor(c.editor);
16185 //        }
16186
16187         this.lookup[c.id] = c;
16188     }
16189
16190     /**
16191      * The width of columns which have no width specified (defaults to 100)
16192      * @type Number
16193      */
16194     this.defaultWidth = 100;
16195
16196     /**
16197      * Default sortable of columns which have no sortable specified (defaults to false)
16198      * @type Boolean
16199      */
16200     this.defaultSortable = false;
16201
16202     this.addEvents({
16203         /**
16204              * @event widthchange
16205              * Fires when the width of a column changes.
16206              * @param {ColumnModel} this
16207              * @param {Number} columnIndex The column index
16208              * @param {Number} newWidth The new width
16209              */
16210             "widthchange": true,
16211         /**
16212              * @event headerchange
16213              * Fires when the text of a header changes.
16214              * @param {ColumnModel} this
16215              * @param {Number} columnIndex The column index
16216              * @param {Number} newText The new header text
16217              */
16218             "headerchange": true,
16219         /**
16220              * @event hiddenchange
16221              * Fires when a column is hidden or "unhidden".
16222              * @param {ColumnModel} this
16223              * @param {Number} columnIndex The column index
16224              * @param {Boolean} hidden true if hidden, false otherwise
16225              */
16226             "hiddenchange": true,
16227             /**
16228          * @event columnmoved
16229          * Fires when a column is moved.
16230          * @param {ColumnModel} this
16231          * @param {Number} oldIndex
16232          * @param {Number} newIndex
16233          */
16234         "columnmoved" : true,
16235         /**
16236          * @event columlockchange
16237          * Fires when a column's locked state is changed
16238          * @param {ColumnModel} this
16239          * @param {Number} colIndex
16240          * @param {Boolean} locked true if locked
16241          */
16242         "columnlockchange" : true
16243     });
16244     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16245 };
16246 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16247     /**
16248      * @cfg {String} header The header text to display in the Grid view.
16249      */
16250     /**
16251      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16252      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16253      * specified, the column's index is used as an index into the Record's data Array.
16254      */
16255     /**
16256      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16257      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16258      */
16259     /**
16260      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16261      * Defaults to the value of the {@link #defaultSortable} property.
16262      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16263      */
16264     /**
16265      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16266      */
16267     /**
16268      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16269      */
16270     /**
16271      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16272      */
16273     /**
16274      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16275      */
16276     /**
16277      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16278      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16279      * default renderer uses the raw data value.
16280      */
16281     /**
16282      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16283      */
16284
16285     /**
16286      * Returns the id of the column at the specified index.
16287      * @param {Number} index The column index
16288      * @return {String} the id
16289      */
16290     getColumnId : function(index){
16291         return this.config[index].id;
16292     },
16293
16294     /**
16295      * Returns the column for a specified id.
16296      * @param {String} id The column id
16297      * @return {Object} the column
16298      */
16299     getColumnById : function(id){
16300         return this.lookup[id];
16301     },
16302
16303     
16304     /**
16305      * Returns the column for a specified dataIndex.
16306      * @param {String} dataIndex The column dataIndex
16307      * @return {Object|Boolean} the column or false if not found
16308      */
16309     getColumnByDataIndex: function(dataIndex){
16310         var index = this.findColumnIndex(dataIndex);
16311         return index > -1 ? this.config[index] : false;
16312     },
16313     
16314     /**
16315      * Returns the index for a specified column id.
16316      * @param {String} id The column id
16317      * @return {Number} the index, or -1 if not found
16318      */
16319     getIndexById : function(id){
16320         for(var i = 0, len = this.config.length; i < len; i++){
16321             if(this.config[i].id == id){
16322                 return i;
16323             }
16324         }
16325         return -1;
16326     },
16327     
16328     /**
16329      * Returns the index for a specified column dataIndex.
16330      * @param {String} dataIndex The column dataIndex
16331      * @return {Number} the index, or -1 if not found
16332      */
16333     
16334     findColumnIndex : function(dataIndex){
16335         for(var i = 0, len = this.config.length; i < len; i++){
16336             if(this.config[i].dataIndex == dataIndex){
16337                 return i;
16338             }
16339         }
16340         return -1;
16341     },
16342     
16343     
16344     moveColumn : function(oldIndex, newIndex){
16345         var c = this.config[oldIndex];
16346         this.config.splice(oldIndex, 1);
16347         this.config.splice(newIndex, 0, c);
16348         this.dataMap = null;
16349         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16350     },
16351
16352     isLocked : function(colIndex){
16353         return this.config[colIndex].locked === true;
16354     },
16355
16356     setLocked : function(colIndex, value, suppressEvent){
16357         if(this.isLocked(colIndex) == value){
16358             return;
16359         }
16360         this.config[colIndex].locked = value;
16361         if(!suppressEvent){
16362             this.fireEvent("columnlockchange", this, colIndex, value);
16363         }
16364     },
16365
16366     getTotalLockedWidth : function(){
16367         var totalWidth = 0;
16368         for(var i = 0; i < this.config.length; i++){
16369             if(this.isLocked(i) && !this.isHidden(i)){
16370                 this.totalWidth += this.getColumnWidth(i);
16371             }
16372         }
16373         return totalWidth;
16374     },
16375
16376     getLockedCount : function(){
16377         for(var i = 0, len = this.config.length; i < len; i++){
16378             if(!this.isLocked(i)){
16379                 return i;
16380             }
16381         }
16382     },
16383
16384     /**
16385      * Returns the number of columns.
16386      * @return {Number}
16387      */
16388     getColumnCount : function(visibleOnly){
16389         if(visibleOnly === true){
16390             var c = 0;
16391             for(var i = 0, len = this.config.length; i < len; i++){
16392                 if(!this.isHidden(i)){
16393                     c++;
16394                 }
16395             }
16396             return c;
16397         }
16398         return this.config.length;
16399     },
16400
16401     /**
16402      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16403      * @param {Function} fn
16404      * @param {Object} scope (optional)
16405      * @return {Array} result
16406      */
16407     getColumnsBy : function(fn, scope){
16408         var r = [];
16409         for(var i = 0, len = this.config.length; i < len; i++){
16410             var c = this.config[i];
16411             if(fn.call(scope||this, c, i) === true){
16412                 r[r.length] = c;
16413             }
16414         }
16415         return r;
16416     },
16417
16418     /**
16419      * Returns true if the specified column is sortable.
16420      * @param {Number} col The column index
16421      * @return {Boolean}
16422      */
16423     isSortable : function(col){
16424         if(typeof this.config[col].sortable == "undefined"){
16425             return this.defaultSortable;
16426         }
16427         return this.config[col].sortable;
16428     },
16429
16430     /**
16431      * Returns the rendering (formatting) function defined for the column.
16432      * @param {Number} col The column index.
16433      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16434      */
16435     getRenderer : function(col){
16436         if(!this.config[col].renderer){
16437             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16438         }
16439         return this.config[col].renderer;
16440     },
16441
16442     /**
16443      * Sets the rendering (formatting) function for a column.
16444      * @param {Number} col The column index
16445      * @param {Function} fn The function to use to process the cell's raw data
16446      * to return HTML markup for the grid view. The render function is called with
16447      * the following parameters:<ul>
16448      * <li>Data value.</li>
16449      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16450      * <li>css A CSS style string to apply to the table cell.</li>
16451      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16452      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16453      * <li>Row index</li>
16454      * <li>Column index</li>
16455      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16456      */
16457     setRenderer : function(col, fn){
16458         this.config[col].renderer = fn;
16459     },
16460
16461     /**
16462      * Returns the width for the specified column.
16463      * @param {Number} col The column index
16464      * @return {Number}
16465      */
16466     getColumnWidth : function(col){
16467         return this.config[col].width * 1 || this.defaultWidth;
16468     },
16469
16470     /**
16471      * Sets the width for a column.
16472      * @param {Number} col The column index
16473      * @param {Number} width The new width
16474      */
16475     setColumnWidth : function(col, width, suppressEvent){
16476         this.config[col].width = width;
16477         this.totalWidth = null;
16478         if(!suppressEvent){
16479              this.fireEvent("widthchange", this, col, width);
16480         }
16481     },
16482
16483     /**
16484      * Returns the total width of all columns.
16485      * @param {Boolean} includeHidden True to include hidden column widths
16486      * @return {Number}
16487      */
16488     getTotalWidth : function(includeHidden){
16489         if(!this.totalWidth){
16490             this.totalWidth = 0;
16491             for(var i = 0, len = this.config.length; i < len; i++){
16492                 if(includeHidden || !this.isHidden(i)){
16493                     this.totalWidth += this.getColumnWidth(i);
16494                 }
16495             }
16496         }
16497         return this.totalWidth;
16498     },
16499
16500     /**
16501      * Returns the header for the specified column.
16502      * @param {Number} col The column index
16503      * @return {String}
16504      */
16505     getColumnHeader : function(col){
16506         return this.config[col].header;
16507     },
16508
16509     /**
16510      * Sets the header for a column.
16511      * @param {Number} col The column index
16512      * @param {String} header The new header
16513      */
16514     setColumnHeader : function(col, header){
16515         this.config[col].header = header;
16516         this.fireEvent("headerchange", this, col, header);
16517     },
16518
16519     /**
16520      * Returns the tooltip for the specified column.
16521      * @param {Number} col The column index
16522      * @return {String}
16523      */
16524     getColumnTooltip : function(col){
16525             return this.config[col].tooltip;
16526     },
16527     /**
16528      * Sets the tooltip for a column.
16529      * @param {Number} col The column index
16530      * @param {String} tooltip The new tooltip
16531      */
16532     setColumnTooltip : function(col, tooltip){
16533             this.config[col].tooltip = tooltip;
16534     },
16535
16536     /**
16537      * Returns the dataIndex for the specified column.
16538      * @param {Number} col The column index
16539      * @return {Number}
16540      */
16541     getDataIndex : function(col){
16542         return this.config[col].dataIndex;
16543     },
16544
16545     /**
16546      * Sets the dataIndex for a column.
16547      * @param {Number} col The column index
16548      * @param {Number} dataIndex The new dataIndex
16549      */
16550     setDataIndex : function(col, dataIndex){
16551         this.config[col].dataIndex = dataIndex;
16552     },
16553
16554     
16555     
16556     /**
16557      * Returns true if the cell is editable.
16558      * @param {Number} colIndex The column index
16559      * @param {Number} rowIndex The row index
16560      * @return {Boolean}
16561      */
16562     isCellEditable : function(colIndex, rowIndex){
16563         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16564     },
16565
16566     /**
16567      * Returns the editor defined for the cell/column.
16568      * return false or null to disable editing.
16569      * @param {Number} colIndex The column index
16570      * @param {Number} rowIndex The row index
16571      * @return {Object}
16572      */
16573     getCellEditor : function(colIndex, rowIndex){
16574         return this.config[colIndex].editor;
16575     },
16576
16577     /**
16578      * Sets if a column is editable.
16579      * @param {Number} col The column index
16580      * @param {Boolean} editable True if the column is editable
16581      */
16582     setEditable : function(col, editable){
16583         this.config[col].editable = editable;
16584     },
16585
16586
16587     /**
16588      * Returns true if the column is hidden.
16589      * @param {Number} colIndex The column index
16590      * @return {Boolean}
16591      */
16592     isHidden : function(colIndex){
16593         return this.config[colIndex].hidden;
16594     },
16595
16596
16597     /**
16598      * Returns true if the column width cannot be changed
16599      */
16600     isFixed : function(colIndex){
16601         return this.config[colIndex].fixed;
16602     },
16603
16604     /**
16605      * Returns true if the column can be resized
16606      * @return {Boolean}
16607      */
16608     isResizable : function(colIndex){
16609         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16610     },
16611     /**
16612      * Sets if a column is hidden.
16613      * @param {Number} colIndex The column index
16614      * @param {Boolean} hidden True if the column is hidden
16615      */
16616     setHidden : function(colIndex, hidden){
16617         this.config[colIndex].hidden = hidden;
16618         this.totalWidth = null;
16619         this.fireEvent("hiddenchange", this, colIndex, hidden);
16620     },
16621
16622     /**
16623      * Sets the editor for a column.
16624      * @param {Number} col The column index
16625      * @param {Object} editor The editor object
16626      */
16627     setEditor : function(col, editor){
16628         this.config[col].editor = editor;
16629     }
16630 });
16631
16632 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16633         if(typeof value == "string" && value.length < 1){
16634             return "&#160;";
16635         }
16636         return value;
16637 };
16638
16639 // Alias for backwards compatibility
16640 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16641
16642 /**
16643  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16644  * @class Roo.bootstrap.Table.RowSelectionModel
16645  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16646  * It supports multiple selections and keyboard selection/navigation. 
16647  * @constructor
16648  * @param {Object} config
16649  */
16650
16651 Roo.bootstrap.Table.RowSelectionModel = function(config){
16652     Roo.apply(this, config);
16653     this.selections = new Roo.util.MixedCollection(false, function(o){
16654         return o.id;
16655     });
16656
16657     this.last = false;
16658     this.lastActive = false;
16659
16660     this.addEvents({
16661         /**
16662              * @event selectionchange
16663              * Fires when the selection changes
16664              * @param {SelectionModel} this
16665              */
16666             "selectionchange" : true,
16667         /**
16668              * @event afterselectionchange
16669              * Fires after the selection changes (eg. by key press or clicking)
16670              * @param {SelectionModel} this
16671              */
16672             "afterselectionchange" : true,
16673         /**
16674              * @event beforerowselect
16675              * Fires when a row is selected being selected, return false to cancel.
16676              * @param {SelectionModel} this
16677              * @param {Number} rowIndex The selected index
16678              * @param {Boolean} keepExisting False if other selections will be cleared
16679              */
16680             "beforerowselect" : true,
16681         /**
16682              * @event rowselect
16683              * Fires when a row is selected.
16684              * @param {SelectionModel} this
16685              * @param {Number} rowIndex The selected index
16686              * @param {Roo.data.Record} r The record
16687              */
16688             "rowselect" : true,
16689         /**
16690              * @event rowdeselect
16691              * Fires when a row is deselected.
16692              * @param {SelectionModel} this
16693              * @param {Number} rowIndex The selected index
16694              */
16695         "rowdeselect" : true
16696     });
16697     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16698     this.locked = false;
16699 };
16700
16701 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16702     /**
16703      * @cfg {Boolean} singleSelect
16704      * True to allow selection of only one row at a time (defaults to false)
16705      */
16706     singleSelect : false,
16707
16708     // private
16709     initEvents : function(){
16710
16711         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16712             this.grid.on("mousedown", this.handleMouseDown, this);
16713         }else{ // allow click to work like normal
16714             this.grid.on("rowclick", this.handleDragableRowClick, this);
16715         }
16716
16717         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16718             "up" : function(e){
16719                 if(!e.shiftKey){
16720                     this.selectPrevious(e.shiftKey);
16721                 }else if(this.last !== false && this.lastActive !== false){
16722                     var last = this.last;
16723                     this.selectRange(this.last,  this.lastActive-1);
16724                     this.grid.getView().focusRow(this.lastActive);
16725                     if(last !== false){
16726                         this.last = last;
16727                     }
16728                 }else{
16729                     this.selectFirstRow();
16730                 }
16731                 this.fireEvent("afterselectionchange", this);
16732             },
16733             "down" : function(e){
16734                 if(!e.shiftKey){
16735                     this.selectNext(e.shiftKey);
16736                 }else if(this.last !== false && this.lastActive !== false){
16737                     var last = this.last;
16738                     this.selectRange(this.last,  this.lastActive+1);
16739                     this.grid.getView().focusRow(this.lastActive);
16740                     if(last !== false){
16741                         this.last = last;
16742                     }
16743                 }else{
16744                     this.selectFirstRow();
16745                 }
16746                 this.fireEvent("afterselectionchange", this);
16747             },
16748             scope: this
16749         });
16750
16751         var view = this.grid.view;
16752         view.on("refresh", this.onRefresh, this);
16753         view.on("rowupdated", this.onRowUpdated, this);
16754         view.on("rowremoved", this.onRemove, this);
16755     },
16756
16757     // private
16758     onRefresh : function(){
16759         var ds = this.grid.dataSource, i, v = this.grid.view;
16760         var s = this.selections;
16761         s.each(function(r){
16762             if((i = ds.indexOfId(r.id)) != -1){
16763                 v.onRowSelect(i);
16764             }else{
16765                 s.remove(r);
16766             }
16767         });
16768     },
16769
16770     // private
16771     onRemove : function(v, index, r){
16772         this.selections.remove(r);
16773     },
16774
16775     // private
16776     onRowUpdated : function(v, index, r){
16777         if(this.isSelected(r)){
16778             v.onRowSelect(index);
16779         }
16780     },
16781
16782     /**
16783      * Select records.
16784      * @param {Array} records The records to select
16785      * @param {Boolean} keepExisting (optional) True to keep existing selections
16786      */
16787     selectRecords : function(records, keepExisting){
16788         if(!keepExisting){
16789             this.clearSelections();
16790         }
16791         var ds = this.grid.dataSource;
16792         for(var i = 0, len = records.length; i < len; i++){
16793             this.selectRow(ds.indexOf(records[i]), true);
16794         }
16795     },
16796
16797     /**
16798      * Gets the number of selected rows.
16799      * @return {Number}
16800      */
16801     getCount : function(){
16802         return this.selections.length;
16803     },
16804
16805     /**
16806      * Selects the first row in the grid.
16807      */
16808     selectFirstRow : function(){
16809         this.selectRow(0);
16810     },
16811
16812     /**
16813      * Select the last row.
16814      * @param {Boolean} keepExisting (optional) True to keep existing selections
16815      */
16816     selectLastRow : function(keepExisting){
16817         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16818     },
16819
16820     /**
16821      * Selects the row immediately following the last selected row.
16822      * @param {Boolean} keepExisting (optional) True to keep existing selections
16823      */
16824     selectNext : function(keepExisting){
16825         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16826             this.selectRow(this.last+1, keepExisting);
16827             this.grid.getView().focusRow(this.last);
16828         }
16829     },
16830
16831     /**
16832      * Selects the row that precedes the last selected row.
16833      * @param {Boolean} keepExisting (optional) True to keep existing selections
16834      */
16835     selectPrevious : function(keepExisting){
16836         if(this.last){
16837             this.selectRow(this.last-1, keepExisting);
16838             this.grid.getView().focusRow(this.last);
16839         }
16840     },
16841
16842     /**
16843      * Returns the selected records
16844      * @return {Array} Array of selected records
16845      */
16846     getSelections : function(){
16847         return [].concat(this.selections.items);
16848     },
16849
16850     /**
16851      * Returns the first selected record.
16852      * @return {Record}
16853      */
16854     getSelected : function(){
16855         return this.selections.itemAt(0);
16856     },
16857
16858
16859     /**
16860      * Clears all selections.
16861      */
16862     clearSelections : function(fast){
16863         if(this.locked) return;
16864         if(fast !== true){
16865             var ds = this.grid.dataSource;
16866             var s = this.selections;
16867             s.each(function(r){
16868                 this.deselectRow(ds.indexOfId(r.id));
16869             }, this);
16870             s.clear();
16871         }else{
16872             this.selections.clear();
16873         }
16874         this.last = false;
16875     },
16876
16877
16878     /**
16879      * Selects all rows.
16880      */
16881     selectAll : function(){
16882         if(this.locked) return;
16883         this.selections.clear();
16884         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16885             this.selectRow(i, true);
16886         }
16887     },
16888
16889     /**
16890      * Returns True if there is a selection.
16891      * @return {Boolean}
16892      */
16893     hasSelection : function(){
16894         return this.selections.length > 0;
16895     },
16896
16897     /**
16898      * Returns True if the specified row is selected.
16899      * @param {Number/Record} record The record or index of the record to check
16900      * @return {Boolean}
16901      */
16902     isSelected : function(index){
16903         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16904         return (r && this.selections.key(r.id) ? true : false);
16905     },
16906
16907     /**
16908      * Returns True if the specified record id is selected.
16909      * @param {String} id The id of record to check
16910      * @return {Boolean}
16911      */
16912     isIdSelected : function(id){
16913         return (this.selections.key(id) ? true : false);
16914     },
16915
16916     // private
16917     handleMouseDown : function(e, t){
16918         var view = this.grid.getView(), rowIndex;
16919         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16920             return;
16921         };
16922         if(e.shiftKey && this.last !== false){
16923             var last = this.last;
16924             this.selectRange(last, rowIndex, e.ctrlKey);
16925             this.last = last; // reset the last
16926             view.focusRow(rowIndex);
16927         }else{
16928             var isSelected = this.isSelected(rowIndex);
16929             if(e.button !== 0 && isSelected){
16930                 view.focusRow(rowIndex);
16931             }else if(e.ctrlKey && isSelected){
16932                 this.deselectRow(rowIndex);
16933             }else if(!isSelected){
16934                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16935                 view.focusRow(rowIndex);
16936             }
16937         }
16938         this.fireEvent("afterselectionchange", this);
16939     },
16940     // private
16941     handleDragableRowClick :  function(grid, rowIndex, e) 
16942     {
16943         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16944             this.selectRow(rowIndex, false);
16945             grid.view.focusRow(rowIndex);
16946              this.fireEvent("afterselectionchange", this);
16947         }
16948     },
16949     
16950     /**
16951      * Selects multiple rows.
16952      * @param {Array} rows Array of the indexes of the row to select
16953      * @param {Boolean} keepExisting (optional) True to keep existing selections
16954      */
16955     selectRows : function(rows, keepExisting){
16956         if(!keepExisting){
16957             this.clearSelections();
16958         }
16959         for(var i = 0, len = rows.length; i < len; i++){
16960             this.selectRow(rows[i], true);
16961         }
16962     },
16963
16964     /**
16965      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16966      * @param {Number} startRow The index of the first row in the range
16967      * @param {Number} endRow The index of the last row in the range
16968      * @param {Boolean} keepExisting (optional) True to retain existing selections
16969      */
16970     selectRange : function(startRow, endRow, keepExisting){
16971         if(this.locked) return;
16972         if(!keepExisting){
16973             this.clearSelections();
16974         }
16975         if(startRow <= endRow){
16976             for(var i = startRow; i <= endRow; i++){
16977                 this.selectRow(i, true);
16978             }
16979         }else{
16980             for(var i = startRow; i >= endRow; i--){
16981                 this.selectRow(i, true);
16982             }
16983         }
16984     },
16985
16986     /**
16987      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16988      * @param {Number} startRow The index of the first row in the range
16989      * @param {Number} endRow The index of the last row in the range
16990      */
16991     deselectRange : function(startRow, endRow, preventViewNotify){
16992         if(this.locked) return;
16993         for(var i = startRow; i <= endRow; i++){
16994             this.deselectRow(i, preventViewNotify);
16995         }
16996     },
16997
16998     /**
16999      * Selects a row.
17000      * @param {Number} row The index of the row to select
17001      * @param {Boolean} keepExisting (optional) True to keep existing selections
17002      */
17003     selectRow : function(index, keepExisting, preventViewNotify){
17004         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17005         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17006             if(!keepExisting || this.singleSelect){
17007                 this.clearSelections();
17008             }
17009             var r = this.grid.dataSource.getAt(index);
17010             this.selections.add(r);
17011             this.last = this.lastActive = index;
17012             if(!preventViewNotify){
17013                 this.grid.getView().onRowSelect(index);
17014             }
17015             this.fireEvent("rowselect", this, index, r);
17016             this.fireEvent("selectionchange", this);
17017         }
17018     },
17019
17020     /**
17021      * Deselects a row.
17022      * @param {Number} row The index of the row to deselect
17023      */
17024     deselectRow : function(index, preventViewNotify){
17025         if(this.locked) return;
17026         if(this.last == index){
17027             this.last = false;
17028         }
17029         if(this.lastActive == index){
17030             this.lastActive = false;
17031         }
17032         var r = this.grid.dataSource.getAt(index);
17033         this.selections.remove(r);
17034         if(!preventViewNotify){
17035             this.grid.getView().onRowDeselect(index);
17036         }
17037         this.fireEvent("rowdeselect", this, index);
17038         this.fireEvent("selectionchange", this);
17039     },
17040
17041     // private
17042     restoreLast : function(){
17043         if(this._last){
17044             this.last = this._last;
17045         }
17046     },
17047
17048     // private
17049     acceptsNav : function(row, col, cm){
17050         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17051     },
17052
17053     // private
17054     onEditorKey : function(field, e){
17055         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17056         if(k == e.TAB){
17057             e.stopEvent();
17058             ed.completeEdit();
17059             if(e.shiftKey){
17060                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17061             }else{
17062                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17063             }
17064         }else if(k == e.ENTER && !e.ctrlKey){
17065             e.stopEvent();
17066             ed.completeEdit();
17067             if(e.shiftKey){
17068                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17069             }else{
17070                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17071             }
17072         }else if(k == e.ESC){
17073             ed.cancelEdit();
17074         }
17075         if(newCell){
17076             g.startEditing(newCell[0], newCell[1]);
17077         }
17078     }
17079 });/*
17080  * - LGPL
17081  *
17082  * element
17083  * 
17084  */
17085
17086 /**
17087  * @class Roo.bootstrap.MessageBar
17088  * @extends Roo.bootstrap.Component
17089  * Bootstrap MessageBar class
17090  * @cfg {String} html contents of the MessageBar
17091  * @cfg {String} weight (info | success | warning | danger) default info
17092  * @cfg {String} beforeClass insert the bar before the given class
17093  * @cfg {Boolean} closable (true | false) default false
17094  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17095  * 
17096  * @constructor
17097  * Create a new Element
17098  * @param {Object} config The config object
17099  */
17100
17101 Roo.bootstrap.MessageBar = function(config){
17102     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17103 };
17104
17105 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17106     
17107     html: '',
17108     weight: 'info',
17109     closable: false,
17110     fixed: false,
17111     beforeClass: 'bootstrap-sticky-wrap',
17112     
17113     getAutoCreate : function(){
17114         
17115         var cfg = {
17116             tag: 'div',
17117             cls: 'alert alert-dismissable alert-' + this.weight,
17118             cn: [
17119                 {
17120                     tag: 'span',
17121                     cls: 'message',
17122                     html: this.html || ''
17123                 }
17124             ]
17125         }
17126         
17127         if(this.fixed){
17128             cfg.cls += ' alert-messages-fixed';
17129         }
17130         
17131         if(this.closable){
17132             cfg.cn.push({
17133                 tag: 'button',
17134                 cls: 'close',
17135                 html: 'x'
17136             });
17137         }
17138         
17139         return cfg;
17140     },
17141     
17142     onRender : function(ct, position)
17143     {
17144         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17145         
17146         if(!this.el){
17147             var cfg = Roo.apply({},  this.getAutoCreate());
17148             cfg.id = Roo.id();
17149             
17150             if (this.cls) {
17151                 cfg.cls += ' ' + this.cls;
17152             }
17153             if (this.style) {
17154                 cfg.style = this.style;
17155             }
17156             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17157             
17158             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17159         }
17160         
17161         this.el.select('>button.close').on('click', this.hide, this);
17162         
17163     },
17164     
17165     show : function()
17166     {
17167         if (!this.rendered) {
17168             this.render();
17169         }
17170         
17171         this.el.show();
17172         
17173         this.fireEvent('show', this);
17174         
17175     },
17176     
17177     hide : function()
17178     {
17179         if (!this.rendered) {
17180             this.render();
17181         }
17182         
17183         this.el.hide();
17184         
17185         this.fireEvent('hide', this);
17186     },
17187     
17188     update : function()
17189     {
17190 //        var e = this.el.dom.firstChild;
17191 //        
17192 //        if(this.closable){
17193 //            e = e.nextSibling;
17194 //        }
17195 //        
17196 //        e.data = this.html || '';
17197
17198         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17199     }
17200    
17201 });
17202
17203  
17204
17205