Roo/bootstrap/CheckBox.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         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @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
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     hide: function() {
746        
747      
748         this.el.hide();   
749     },
750     show: function() {
751        
752         this.el.show();   
753     }
754     
755     
756 });
757
758  /*
759  * - LGPL
760  *
761  * column
762  * 
763  */
764
765 /**
766  * @class Roo.bootstrap.Column
767  * @extends Roo.bootstrap.Component
768  * Bootstrap Column class
769  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
770  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
771  * @cfg {Number} md colspan out of 12 for computer-sized screens
772  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
773  * @cfg {String} html content of column.
774  * 
775  * @constructor
776  * Create a new Column
777  * @param {Object} config The config object
778  */
779
780 Roo.bootstrap.Column = function(config){
781     Roo.bootstrap.Column.superclass.constructor.call(this, config);
782 };
783
784 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
785     
786     xs: null,
787     sm: null,
788     md: null,
789     lg: null,
790     html: '',
791     offset: 0,
792     
793     getAutoCreate : function(){
794         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
795         
796         cfg = {
797             tag: 'div',
798             cls: 'column'
799         };
800         
801         var settings=this;
802         ['xs','sm','md','lg'].map(function(size){
803             if (settings[size]) {
804                 cfg.cls += ' col-' + size + '-' + settings[size];
805             }
806         });
807         if (this.html.length) {
808             cfg.html = this.html;
809         }
810         
811         return cfg;
812     }
813    
814 });
815
816  
817
818  /*
819  * - LGPL
820  *
821  * page container.
822  * 
823  */
824
825
826 /**
827  * @class Roo.bootstrap.Container
828  * @extends Roo.bootstrap.Component
829  * Bootstrap Container class
830  * @cfg {Boolean} jumbotron is it a jumbotron element
831  * @cfg {String} html content of element
832  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
833  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
834  * @cfg {String} header content of header (for panel)
835  * @cfg {String} footer content of footer (for panel)
836  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
837  * @cfg {String} tag (header|aside|section) type of HTML tag.
838
839  *     
840  * @constructor
841  * Create a new Container
842  * @param {Object} config The config object
843  */
844
845 Roo.bootstrap.Container = function(config){
846     Roo.bootstrap.Container.superclass.constructor.call(this, config);
847 };
848
849 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
850     
851     jumbotron : false,
852     well: '',
853     panel : '',
854     header: '',
855     footer : '',
856     sticky: '',
857     tag : false,
858   
859      
860     getChildContainer : function() {
861         
862         if(!this.el){
863             return false;
864         }
865         
866         if (this.panel.length) {
867             return this.el.select('.panel-body',true).first();
868         }
869         
870         return this.el;
871     },
872     
873     
874     getAutoCreate : function(){
875         
876         var cfg = {
877             tag : this.tag || 'div',
878             html : '',
879             cls : ''
880         };
881         if (this.jumbotron) {
882             cfg.cls = 'jumbotron';
883         }
884         // - this is applied by the parent..
885         //if (this.cls) {
886         //    cfg.cls = this.cls + '';
887         //}
888         
889         if (this.sticky.length) {
890             
891             var bd = Roo.get(document.body);
892             if (!bd.hasClass('bootstrap-sticky')) {
893                 bd.addClass('bootstrap-sticky');
894                 Roo.select('html',true).setStyle('height', '100%');
895             }
896              
897             cfg.cls += 'bootstrap-sticky-' + this.sticky;
898         }
899         
900         
901         if (this.well.length) {
902             switch (this.well) {
903                 case 'lg':
904                 case 'sm':
905                     cfg.cls +=' well well-' +this.well;
906                     break;
907                 default:
908                     cfg.cls +=' well';
909                     break;
910             }
911         }
912         
913         var body = cfg;
914         
915         if (this.panel.length) {
916             cfg.cls += ' panel panel-' + this.panel;
917             cfg.cn = [];
918             if (this.header.length) {
919                 cfg.cn.push({
920                     
921                     cls : 'panel-heading',
922                     cn : [{
923                         tag: 'h3',
924                         cls : 'panel-title',
925                         html : this.header
926                     }]
927                     
928                 });
929             }
930             body = false;
931             cfg.cn.push({
932                 cls : 'panel-body',
933                 html : this.html
934             });
935             
936             
937             if (this.footer.length) {
938                 cfg.cn.push({
939                     cls : 'panel-footer',
940                     html : this.footer
941                     
942                 });
943             }
944             
945         }
946         
947         if (body) {
948             body.html = this.html || cfg.html;
949         }
950         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
951             cfg.cls =  'container';
952         }
953         
954         return cfg;
955     }
956    
957 });
958
959  /*
960  * - LGPL
961  *
962  * image
963  * 
964  */
965
966
967 /**
968  * @class Roo.bootstrap.Img
969  * @extends Roo.bootstrap.Component
970  * Bootstrap Img class
971  * @cfg {Boolean} imgResponsive false | true
972  * @cfg {String} border rounded | circle | thumbnail
973  * @cfg {String} src image source
974  * @cfg {String} alt image alternative text
975  * @cfg {String} href a tag href
976  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
977  * 
978  * @constructor
979  * Create a new Input
980  * @param {Object} config The config object
981  */
982
983 Roo.bootstrap.Img = function(config){
984     Roo.bootstrap.Img.superclass.constructor.call(this, config);
985     
986     this.addEvents({
987         // img events
988         /**
989          * @event click
990          * The img click event for the img.
991          * @param {Roo.EventObject} e
992          */
993         "click" : true
994     });
995 };
996
997 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
998     
999     imgResponsive: true,
1000     border: '',
1001     src: '',
1002     href: false,
1003     target: false,
1004
1005     getAutoCreate : function(){
1006         
1007         var cfg = {
1008             tag: 'img',
1009             cls: (this.imgResponsive) ? 'img-responsive' : '',
1010             html : null
1011         }
1012         
1013         cfg.html = this.html || cfg.html;
1014         
1015         cfg.src = this.src || cfg.src;
1016         
1017         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1018             cfg.cls += ' img-' + this.border;
1019         }
1020         
1021         if(this.alt){
1022             cfg.alt = this.alt;
1023         }
1024         
1025         if(this.href){
1026             var a = {
1027                 tag: 'a',
1028                 href: this.href,
1029                 cn: [
1030                     cfg
1031                 ]
1032             }
1033             
1034             if(this.target){
1035                 a.target = this.target;
1036             }
1037             
1038         }
1039         
1040         
1041         return (this.href) ? a : cfg;
1042     },
1043     
1044     initEvents: function() {
1045         
1046         if(!this.href){
1047             this.el.on('click', this.onClick, this);
1048         }
1049     },
1050     
1051     onClick : function(e)
1052     {
1053         Roo.log('img onclick');
1054         this.fireEvent('click', this, e);
1055     }
1056    
1057 });
1058
1059  /*
1060  * - LGPL
1061  *
1062  * image
1063  * 
1064  */
1065
1066
1067 /**
1068  * @class Roo.bootstrap.Link
1069  * @extends Roo.bootstrap.Component
1070  * Bootstrap Link Class
1071  * @cfg {String} alt image alternative text
1072  * @cfg {String} href a tag href
1073  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1074  * @cfg {String} html the content of the link.
1075
1076  * 
1077  * @constructor
1078  * Create a new Input
1079  * @param {Object} config The config object
1080  */
1081
1082 Roo.bootstrap.Link = function(config){
1083     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1084     
1085     this.addEvents({
1086         // img events
1087         /**
1088          * @event click
1089          * The img click event for the img.
1090          * @param {Roo.EventObject} e
1091          */
1092         "click" : true
1093     });
1094 };
1095
1096 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1097     
1098     href: false,
1099     target: false,
1100
1101     getAutoCreate : function(){
1102         
1103         var cfg = {
1104             tag: 'a',
1105             html : this.html || 'html-missing'
1106         }
1107         
1108         
1109         if(this.alt){
1110             cfg.alt = this.alt;
1111         }
1112         cfg.href = this.href || '#';
1113         if(this.target){
1114             cfg.target = this.target;
1115         }
1116         
1117         return cfg;
1118     },
1119     
1120     initEvents: function() {
1121         
1122         if(!this.href){
1123             this.el.on('click', this.onClick, this);
1124         }
1125     },
1126     
1127     onClick : function(e)
1128     {
1129         //Roo.log('img onclick');
1130         this.fireEvent('click', this, e);
1131     }
1132    
1133 });
1134
1135  /*
1136  * - LGPL
1137  *
1138  * header
1139  * 
1140  */
1141
1142 /**
1143  * @class Roo.bootstrap.Header
1144  * @extends Roo.bootstrap.Component
1145  * Bootstrap Header class
1146  * @cfg {String} html content of header
1147  * @cfg {Number} level (1|2|3|4|5|6) default 1
1148  * 
1149  * @constructor
1150  * Create a new Header
1151  * @param {Object} config The config object
1152  */
1153
1154
1155 Roo.bootstrap.Header  = function(config){
1156     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1157 };
1158
1159 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1160     
1161     //href : false,
1162     html : false,
1163     level : 1,
1164     
1165     
1166     
1167     getAutoCreate : function(){
1168         
1169         var cfg = {
1170             tag: 'h' + (1 *this.level),
1171             html: this.html || 'fill in html'
1172         } ;
1173         
1174         return cfg;
1175     }
1176    
1177 });
1178
1179  
1180
1181  /*
1182  * Based on:
1183  * Ext JS Library 1.1.1
1184  * Copyright(c) 2006-2007, Ext JS, LLC.
1185  *
1186  * Originally Released Under LGPL - original licence link has changed is not relivant.
1187  *
1188  * Fork - LGPL
1189  * <script type="text/javascript">
1190  */
1191  
1192 /**
1193  * @class Roo.bootstrap.MenuMgr
1194  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1195  * @singleton
1196  */
1197 Roo.bootstrap.MenuMgr = function(){
1198    var menus, active, groups = {}, attached = false, lastShow = new Date();
1199
1200    // private - called when first menu is created
1201    function init(){
1202        menus = {};
1203        active = new Roo.util.MixedCollection();
1204        Roo.get(document).addKeyListener(27, function(){
1205            if(active.length > 0){
1206                hideAll();
1207            }
1208        });
1209    }
1210
1211    // private
1212    function hideAll(){
1213        if(active && active.length > 0){
1214            var c = active.clone();
1215            c.each(function(m){
1216                m.hide();
1217            });
1218        }
1219    }
1220
1221    // private
1222    function onHide(m){
1223        active.remove(m);
1224        if(active.length < 1){
1225            Roo.get(document).un("mouseup", onMouseDown);
1226             
1227            attached = false;
1228        }
1229    }
1230
1231    // private
1232    function onShow(m){
1233        var last = active.last();
1234        lastShow = new Date();
1235        active.add(m);
1236        if(!attached){
1237           Roo.get(document).on("mouseup", onMouseDown);
1238            
1239            attached = true;
1240        }
1241        if(m.parentMenu){
1242           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1243           m.parentMenu.activeChild = m;
1244        }else if(last && last.isVisible()){
1245           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1246        }
1247    }
1248
1249    // private
1250    function onBeforeHide(m){
1251        if(m.activeChild){
1252            m.activeChild.hide();
1253        }
1254        if(m.autoHideTimer){
1255            clearTimeout(m.autoHideTimer);
1256            delete m.autoHideTimer;
1257        }
1258    }
1259
1260    // private
1261    function onBeforeShow(m){
1262        var pm = m.parentMenu;
1263        if(!pm && !m.allowOtherMenus){
1264            hideAll();
1265        }else if(pm && pm.activeChild && active != m){
1266            pm.activeChild.hide();
1267        }
1268    }
1269
1270    // private
1271    function onMouseDown(e){
1272         Roo.log("on MouseDown");
1273         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1274            hideAll();
1275         }
1276         
1277         
1278    }
1279
1280    // private
1281    function onBeforeCheck(mi, state){
1282        if(state){
1283            var g = groups[mi.group];
1284            for(var i = 0, l = g.length; i < l; i++){
1285                if(g[i] != mi){
1286                    g[i].setChecked(false);
1287                }
1288            }
1289        }
1290    }
1291
1292    return {
1293
1294        /**
1295         * Hides all menus that are currently visible
1296         */
1297        hideAll : function(){
1298             hideAll();  
1299        },
1300
1301        // private
1302        register : function(menu){
1303            if(!menus){
1304                init();
1305            }
1306            menus[menu.id] = menu;
1307            menu.on("beforehide", onBeforeHide);
1308            menu.on("hide", onHide);
1309            menu.on("beforeshow", onBeforeShow);
1310            menu.on("show", onShow);
1311            var g = menu.group;
1312            if(g && menu.events["checkchange"]){
1313                if(!groups[g]){
1314                    groups[g] = [];
1315                }
1316                groups[g].push(menu);
1317                menu.on("checkchange", onCheck);
1318            }
1319        },
1320
1321         /**
1322          * Returns a {@link Roo.menu.Menu} object
1323          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1324          * be used to generate and return a new Menu instance.
1325          */
1326        get : function(menu){
1327            if(typeof menu == "string"){ // menu id
1328                return menus[menu];
1329            }else if(menu.events){  // menu instance
1330                return menu;
1331            }
1332            /*else if(typeof menu.length == 'number'){ // array of menu items?
1333                return new Roo.bootstrap.Menu({items:menu});
1334            }else{ // otherwise, must be a config
1335                return new Roo.bootstrap.Menu(menu);
1336            }
1337            */
1338            return false;
1339        },
1340
1341        // private
1342        unregister : function(menu){
1343            delete menus[menu.id];
1344            menu.un("beforehide", onBeforeHide);
1345            menu.un("hide", onHide);
1346            menu.un("beforeshow", onBeforeShow);
1347            menu.un("show", onShow);
1348            var g = menu.group;
1349            if(g && menu.events["checkchange"]){
1350                groups[g].remove(menu);
1351                menu.un("checkchange", onCheck);
1352            }
1353        },
1354
1355        // private
1356        registerCheckable : function(menuItem){
1357            var g = menuItem.group;
1358            if(g){
1359                if(!groups[g]){
1360                    groups[g] = [];
1361                }
1362                groups[g].push(menuItem);
1363                menuItem.on("beforecheckchange", onBeforeCheck);
1364            }
1365        },
1366
1367        // private
1368        unregisterCheckable : function(menuItem){
1369            var g = menuItem.group;
1370            if(g){
1371                groups[g].remove(menuItem);
1372                menuItem.un("beforecheckchange", onBeforeCheck);
1373            }
1374        }
1375    };
1376 }();/*
1377  * - LGPL
1378  *
1379  * menu
1380  * 
1381  */
1382
1383 /**
1384  * @class Roo.bootstrap.Menu
1385  * @extends Roo.bootstrap.Component
1386  * Bootstrap Menu class - container for MenuItems
1387  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1388  * 
1389  * @constructor
1390  * Create a new Menu
1391  * @param {Object} config The config object
1392  */
1393
1394
1395 Roo.bootstrap.Menu = function(config){
1396     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1397     if (this.registerMenu) {
1398         Roo.bootstrap.MenuMgr.register(this);
1399     }
1400     this.addEvents({
1401         /**
1402          * @event beforeshow
1403          * Fires before this menu is displayed
1404          * @param {Roo.menu.Menu} this
1405          */
1406         beforeshow : true,
1407         /**
1408          * @event beforehide
1409          * Fires before this menu is hidden
1410          * @param {Roo.menu.Menu} this
1411          */
1412         beforehide : true,
1413         /**
1414          * @event show
1415          * Fires after this menu is displayed
1416          * @param {Roo.menu.Menu} this
1417          */
1418         show : true,
1419         /**
1420          * @event hide
1421          * Fires after this menu is hidden
1422          * @param {Roo.menu.Menu} this
1423          */
1424         hide : true,
1425         /**
1426          * @event click
1427          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1428          * @param {Roo.menu.Menu} this
1429          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1430          * @param {Roo.EventObject} e
1431          */
1432         click : true,
1433         /**
1434          * @event mouseover
1435          * Fires when the mouse is hovering over this menu
1436          * @param {Roo.menu.Menu} this
1437          * @param {Roo.EventObject} e
1438          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1439          */
1440         mouseover : true,
1441         /**
1442          * @event mouseout
1443          * Fires when the mouse exits this menu
1444          * @param {Roo.menu.Menu} this
1445          * @param {Roo.EventObject} e
1446          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1447          */
1448         mouseout : true,
1449         /**
1450          * @event itemclick
1451          * Fires when a menu item contained in this menu is clicked
1452          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1453          * @param {Roo.EventObject} e
1454          */
1455         itemclick: true
1456     });
1457     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1458 };
1459
1460 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1461     
1462    /// html : false,
1463     //align : '',
1464     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1465     type: false,
1466     /**
1467      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1468      */
1469     registerMenu : true,
1470     
1471     menuItems :false, // stores the menu items..
1472     
1473     hidden:true,
1474     
1475     parentMenu : false,
1476     
1477     getChildContainer : function() {
1478         return this.el;  
1479     },
1480     
1481     getAutoCreate : function(){
1482          
1483         //if (['right'].indexOf(this.align)!==-1) {
1484         //    cfg.cn[1].cls += ' pull-right'
1485         //}
1486         
1487         
1488         var cfg = {
1489             tag : 'ul',
1490             cls : 'dropdown-menu' ,
1491             style : 'z-index:1000'
1492             
1493         }
1494         
1495         if (this.type === 'submenu') {
1496             cfg.cls = 'submenu active';
1497         }
1498         if (this.type === 'treeview') {
1499             cfg.cls = 'treeview-menu';
1500         }
1501         
1502         return cfg;
1503     },
1504     initEvents : function() {
1505         
1506        // Roo.log("ADD event");
1507        // Roo.log(this.triggerEl.dom);
1508         this.triggerEl.on('click', this.onTriggerPress, this);
1509         this.triggerEl.addClass('dropdown-toggle');
1510         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1511
1512         this.el.on("mouseover", this.onMouseOver, this);
1513         this.el.on("mouseout", this.onMouseOut, this);
1514         
1515         
1516     },
1517     findTargetItem : function(e){
1518         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1519         if(!t){
1520             return false;
1521         }
1522         //Roo.log(t);         Roo.log(t.id);
1523         if(t && t.id){
1524             //Roo.log(this.menuitems);
1525             return this.menuitems.get(t.id);
1526             
1527             //return this.items.get(t.menuItemId);
1528         }
1529         
1530         return false;
1531     },
1532     onClick : function(e){
1533         Roo.log("menu.onClick");
1534         var t = this.findTargetItem(e);
1535         if(!t){
1536             return;
1537         }
1538         Roo.log(e);
1539         /*
1540         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1541             if(t == this.activeItem && t.shouldDeactivate(e)){
1542                 this.activeItem.deactivate();
1543                 delete this.activeItem;
1544                 return;
1545             }
1546             if(t.canActivate){
1547                 this.setActiveItem(t, true);
1548             }
1549             return;
1550             
1551             
1552         }
1553         */
1554         Roo.log('pass click event');
1555         
1556         t.onClick(e);
1557         
1558         this.fireEvent("click", this, t, e);
1559         
1560         this.hide();
1561     },
1562      onMouseOver : function(e){
1563         var t  = this.findTargetItem(e);
1564         //Roo.log(t);
1565         //if(t){
1566         //    if(t.canActivate && !t.disabled){
1567         //        this.setActiveItem(t, true);
1568         //    }
1569         //}
1570         
1571         this.fireEvent("mouseover", this, e, t);
1572     },
1573     isVisible : function(){
1574         return !this.hidden;
1575     },
1576      onMouseOut : function(e){
1577         var t  = this.findTargetItem(e);
1578         
1579         //if(t ){
1580         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1581         //        this.activeItem.deactivate();
1582         //        delete this.activeItem;
1583         //    }
1584         //}
1585         this.fireEvent("mouseout", this, e, t);
1586     },
1587     
1588     
1589     /**
1590      * Displays this menu relative to another element
1591      * @param {String/HTMLElement/Roo.Element} element The element to align to
1592      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1593      * the element (defaults to this.defaultAlign)
1594      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1595      */
1596     show : function(el, pos, parentMenu){
1597         this.parentMenu = parentMenu;
1598         if(!this.el){
1599             this.render();
1600         }
1601         this.fireEvent("beforeshow", this);
1602         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1603     },
1604      /**
1605      * Displays this menu at a specific xy position
1606      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1607      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1608      */
1609     showAt : function(xy, parentMenu, /* private: */_e){
1610         this.parentMenu = parentMenu;
1611         if(!this.el){
1612             this.render();
1613         }
1614         if(_e !== false){
1615             this.fireEvent("beforeshow", this);
1616             
1617             //xy = this.el.adjustForConstraints(xy);
1618         }
1619         //this.el.setXY(xy);
1620         //this.el.show();
1621         this.hideMenuItems();
1622         this.hidden = false;
1623         this.triggerEl.addClass('open');
1624         this.focus();
1625         this.fireEvent("show", this);
1626     },
1627     
1628     focus : function(){
1629         return;
1630         if(!this.hidden){
1631             this.doFocus.defer(50, this);
1632         }
1633     },
1634
1635     doFocus : function(){
1636         if(!this.hidden){
1637             this.focusEl.focus();
1638         }
1639     },
1640
1641     /**
1642      * Hides this menu and optionally all parent menus
1643      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1644      */
1645     hide : function(deep){
1646         
1647         this.hideMenuItems();
1648         if(this.el && this.isVisible()){
1649             this.fireEvent("beforehide", this);
1650             if(this.activeItem){
1651                 this.activeItem.deactivate();
1652                 this.activeItem = null;
1653             }
1654             this.triggerEl.removeClass('open');;
1655             this.hidden = true;
1656             this.fireEvent("hide", this);
1657         }
1658         if(deep === true && this.parentMenu){
1659             this.parentMenu.hide(true);
1660         }
1661     },
1662     
1663     onTriggerPress  : function(e)
1664     {
1665         
1666         Roo.log('trigger press');
1667         //Roo.log(e.getTarget());
1668        // Roo.log(this.triggerEl.dom);
1669         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1670             return;
1671         }
1672         if (this.isVisible()) {
1673             Roo.log('hide');
1674             this.hide();
1675         } else {
1676             this.show(this.triggerEl, false, false);
1677         }
1678         
1679         
1680     },
1681     
1682          
1683        
1684     
1685     hideMenuItems : function()
1686     {
1687         //$(backdrop).remove()
1688         Roo.select('.open',true).each(function(aa) {
1689             
1690             aa.removeClass('open');
1691           //var parent = getParent($(this))
1692           //var relatedTarget = { relatedTarget: this }
1693           
1694            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1695           //if (e.isDefaultPrevented()) return
1696            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1697         })
1698     },
1699     addxtypeChild : function (tree, cntr) {
1700         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1701           
1702         this.menuitems.add(comp);
1703         return comp;
1704
1705     },
1706     getEl : function()
1707     {
1708         Roo.log(this.el);
1709         return this.el;
1710     }
1711 });
1712
1713  
1714  /*
1715  * - LGPL
1716  *
1717  * menu item
1718  * 
1719  */
1720
1721
1722 /**
1723  * @class Roo.bootstrap.MenuItem
1724  * @extends Roo.bootstrap.Component
1725  * Bootstrap MenuItem class
1726  * @cfg {String} html the menu label
1727  * @cfg {String} href the link
1728  * @cfg {Boolean} preventDefault (true | false) default true
1729  * 
1730  * 
1731  * @constructor
1732  * Create a new MenuItem
1733  * @param {Object} config The config object
1734  */
1735
1736
1737 Roo.bootstrap.MenuItem = function(config){
1738     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1739     this.addEvents({
1740         // raw events
1741         /**
1742          * @event click
1743          * The raw click event for the entire grid.
1744          * @param {Roo.EventObject} e
1745          */
1746         "click" : true
1747     });
1748 };
1749
1750 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1751     
1752     href : false,
1753     html : false,
1754     preventDefault: true,
1755     
1756     getAutoCreate : function(){
1757         var cfg= {
1758             tag: 'li',
1759             cls: 'dropdown-menu-item',
1760             cn: [
1761                     {
1762                         tag : 'a',
1763                         href : '#',
1764                         html : 'Link'
1765                     }
1766                 ]
1767         };
1768         if (this.parent().type == 'treeview') {
1769             cfg.cls = 'treeview-menu';
1770         }
1771         
1772         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1773         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1774         return cfg;
1775     },
1776     
1777     initEvents: function() {
1778         
1779         //this.el.select('a').on('click', this.onClick, this);
1780         
1781     },
1782     onClick : function(e)
1783     {
1784         Roo.log('item on click ');
1785         //if(this.preventDefault){
1786         //    e.preventDefault();
1787         //}
1788         //this.parent().hideMenuItems();
1789         
1790         this.fireEvent('click', this, e);
1791     },
1792     getEl : function()
1793     {
1794         return this.el;
1795     }
1796 });
1797
1798  
1799
1800  /*
1801  * - LGPL
1802  *
1803  * menu separator
1804  * 
1805  */
1806
1807
1808 /**
1809  * @class Roo.bootstrap.MenuSeparator
1810  * @extends Roo.bootstrap.Component
1811  * Bootstrap MenuSeparator class
1812  * 
1813  * @constructor
1814  * Create a new MenuItem
1815  * @param {Object} config The config object
1816  */
1817
1818
1819 Roo.bootstrap.MenuSeparator = function(config){
1820     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1821 };
1822
1823 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1824     
1825     getAutoCreate : function(){
1826         var cfg = {
1827             cls: 'divider',
1828             tag : 'li'
1829         };
1830         
1831         return cfg;
1832     }
1833    
1834 });
1835
1836  
1837
1838  
1839 /*
1840 <div class="modal fade">
1841   <div class="modal-dialog">
1842     <div class="modal-content">
1843       <div class="modal-header">
1844         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1845         <h4 class="modal-title">Modal title</h4>
1846       </div>
1847       <div class="modal-body">
1848         <p>One fine body&hellip;</p>
1849       </div>
1850       <div class="modal-footer">
1851         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1852         <button type="button" class="btn btn-primary">Save changes</button>
1853       </div>
1854     </div><!-- /.modal-content -->
1855   </div><!-- /.modal-dialog -->
1856 </div><!-- /.modal -->
1857 */
1858 /*
1859  * - LGPL
1860  *
1861  * page contgainer.
1862  * 
1863  */
1864
1865 /**
1866  * @class Roo.bootstrap.Modal
1867  * @extends Roo.bootstrap.Component
1868  * Bootstrap Modal class
1869  * @cfg {String} title Title of dialog
1870  * @cfg {Boolean} specificTitle (true|false) default false
1871  * @cfg {Array} buttons Array of buttons or standard button set..
1872  * @cfg {String} buttonPosition (left|right|center) default right
1873  * 
1874  * @constructor
1875  * Create a new Modal Dialog
1876  * @param {Object} config The config object
1877  */
1878
1879 Roo.bootstrap.Modal = function(config){
1880     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1881     this.addEvents({
1882         // raw events
1883         /**
1884          * @event btnclick
1885          * The raw btnclick event for the button
1886          * @param {Roo.EventObject} e
1887          */
1888         "btnclick" : true
1889     });
1890     this.buttons = this.buttons || [];
1891 };
1892
1893 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1894     
1895     title : 'test dialog',
1896    
1897     buttons : false,
1898     
1899     // set on load...
1900     body:  false,
1901     
1902     specificTitle: false,
1903     
1904     buttonPosition: 'right',
1905     
1906     onRender : function(ct, position)
1907     {
1908         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1909      
1910         if(!this.el){
1911             var cfg = Roo.apply({},  this.getAutoCreate());
1912             cfg.id = Roo.id();
1913             //if(!cfg.name){
1914             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1915             //}
1916             //if (!cfg.name.length) {
1917             //    delete cfg.name;
1918            // }
1919             if (this.cls) {
1920                 cfg.cls += ' ' + this.cls;
1921             }
1922             if (this.style) {
1923                 cfg.style = this.style;
1924             }
1925             this.el = Roo.get(document.body).createChild(cfg, position);
1926         }
1927         //var type = this.el.dom.type;
1928         
1929         if(this.tabIndex !== undefined){
1930             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1931         }
1932         
1933         
1934         
1935         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1936         this.maskEl.enableDisplayMode("block");
1937         this.maskEl.hide();
1938         //this.el.addClass("x-dlg-modal");
1939     
1940         if (this.buttons.length) {
1941             Roo.each(this.buttons, function(bb) {
1942                 b = Roo.apply({}, bb);
1943                 b.xns = b.xns || Roo.bootstrap;
1944                 b.xtype = b.xtype || 'Button';
1945                 if (typeof(b.listeners) == 'undefined') {
1946                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1947                 }
1948                 
1949                 var btn = Roo.factory(b);
1950                 
1951                 btn.onRender(this.el.select('.modal-footer div').first());
1952                 
1953             },this);
1954         }
1955         // render the children.
1956         var nitems = [];
1957         
1958         if(typeof(this.items) != 'undefined'){
1959             var items = this.items;
1960             delete this.items;
1961
1962             for(var i =0;i < items.length;i++) {
1963                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1964             }
1965         }
1966         
1967         this.items = nitems;
1968         
1969         this.body = this.el.select('.modal-body',true).first();
1970         this.close = this.el.select('.modal-header .close', true).first();
1971         this.footer = this.el.select('.modal-footer',true).first();
1972         this.initEvents();
1973         //this.el.addClass([this.fieldClass, this.cls]);
1974         
1975     },
1976     getAutoCreate : function(){
1977         
1978         
1979         var bdy = {
1980                 cls : 'modal-body',
1981                 html : this.html || ''
1982         };
1983         
1984         var title = {
1985             tag: 'h4',
1986             cls : 'modal-title',
1987             html : this.title
1988         };
1989         
1990         if(this.specificTitle){
1991             title = this.title;
1992         };
1993         
1994         return modal = {
1995             cls: "modal fade",
1996             style : 'display: none',
1997             cn : [
1998                 {
1999                     cls: "modal-dialog",
2000                     cn : [
2001                         {
2002                             cls : "modal-content",
2003                             cn : [
2004                                 {
2005                                     cls : 'modal-header',
2006                                     cn : [
2007                                         {
2008                                             tag: 'button',
2009                                             cls : 'close',
2010                                             html : '&times'
2011                                         },
2012                                         title
2013                                     ]
2014                                 },
2015                                 bdy,
2016                                 {
2017                                     cls : 'modal-footer',
2018                                     cn : [
2019                                         {
2020                                             tag: 'div',
2021                                             cls: 'btn-' + this.buttonPosition
2022                                         }
2023                                     ]
2024                                     
2025                                 }
2026                                 
2027                                 
2028                             ]
2029                             
2030                         }
2031                     ]
2032                         
2033                 }
2034             ]
2035             
2036             
2037         };
2038           
2039     },
2040     getChildContainer : function() {
2041          
2042          return this.el.select('.modal-body',true).first();
2043         
2044     },
2045     getButtonContainer : function() {
2046          return this.el.select('.modal-footer div',true).first();
2047         
2048     },
2049     initEvents : function()
2050     {
2051         this.el.select('.modal-header .close').on('click', this.hide, this);
2052 //        
2053 //        this.addxtype(this);
2054     },
2055     show : function() {
2056         
2057         if (!this.rendered) {
2058             this.render();
2059         }
2060        
2061         this.el.addClass('on');
2062         this.el.removeClass('fade');
2063         this.el.setStyle('display', 'block');
2064         Roo.get(document.body).addClass("x-body-masked");
2065         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2066         this.maskEl.show();
2067         this.el.setStyle('zIndex', '10001');
2068         this.fireEvent('show', this);
2069         
2070         
2071     },
2072     hide : function()
2073     {
2074         Roo.log('Modal hide?!');
2075         this.maskEl.hide();
2076         Roo.get(document.body).removeClass("x-body-masked");
2077         this.el.removeClass('on');
2078         this.el.addClass('fade');
2079         this.el.setStyle('display', 'none');
2080         this.fireEvent('hide', this);
2081     },
2082     
2083     addButton : function(str, cb)
2084     {
2085          
2086         
2087         var b = Roo.apply({}, { html : str } );
2088         b.xns = b.xns || Roo.bootstrap;
2089         b.xtype = b.xtype || 'Button';
2090         if (typeof(b.listeners) == 'undefined') {
2091             b.listeners = { click : cb.createDelegate(this)  };
2092         }
2093         
2094         var btn = Roo.factory(b);
2095            
2096         btn.onRender(this.el.select('.modal-footer div').first());
2097         
2098         return btn;   
2099        
2100     },
2101     
2102     setDefaultButton : function(btn)
2103     {
2104         //this.el.select('.modal-footer').()
2105     },
2106     resizeTo: function(w,h)
2107     {
2108         // skip..
2109     },
2110     setContentSize  : function(w, h)
2111     {
2112         
2113     },
2114     onButtonClick: function(btn,e)
2115     {
2116         //Roo.log([a,b,c]);
2117         this.fireEvent('btnclick', btn.name, e);
2118     },
2119     setTitle: function(str) {
2120         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2121         
2122     }
2123 });
2124
2125
2126 Roo.apply(Roo.bootstrap.Modal,  {
2127     /**
2128          * Button config that displays a single OK button
2129          * @type Object
2130          */
2131         OK :  [{
2132             name : 'ok',
2133             weight : 'primary',
2134             html : 'OK'
2135         }], 
2136         /**
2137          * Button config that displays Yes and No buttons
2138          * @type Object
2139          */
2140         YESNO : [
2141             {
2142                 name  : 'no',
2143                 html : 'No'
2144             },
2145             {
2146                 name  :'yes',
2147                 weight : 'primary',
2148                 html : 'Yes'
2149             }
2150         ],
2151         
2152         /**
2153          * Button config that displays OK and Cancel buttons
2154          * @type Object
2155          */
2156         OKCANCEL : [
2157             {
2158                name : 'cancel',
2159                 html : 'Cancel'
2160             },
2161             {
2162                 name : 'ok',
2163                 weight : 'primary',
2164                 html : 'OK'
2165             }
2166         ],
2167         /**
2168          * Button config that displays Yes, No and Cancel buttons
2169          * @type Object
2170          */
2171         YESNOCANCEL : [
2172             {
2173                 name : 'yes',
2174                 weight : 'primary',
2175                 html : 'Yes'
2176             },
2177             {
2178                 name : 'no',
2179                 html : 'No'
2180             },
2181             {
2182                 name : 'cancel',
2183                 html : 'Cancel'
2184             }
2185         ]
2186 });
2187  /*
2188  * - LGPL
2189  *
2190  * messagebox - can be used as a replace
2191  * 
2192  */
2193 /**
2194  * @class Roo.MessageBox
2195  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2196  * Example usage:
2197  *<pre><code>
2198 // Basic alert:
2199 Roo.Msg.alert('Status', 'Changes saved successfully.');
2200
2201 // Prompt for user data:
2202 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2203     if (btn == 'ok'){
2204         // process text value...
2205     }
2206 });
2207
2208 // Show a dialog using config options:
2209 Roo.Msg.show({
2210    title:'Save Changes?',
2211    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2212    buttons: Roo.Msg.YESNOCANCEL,
2213    fn: processResult,
2214    animEl: 'elId'
2215 });
2216 </code></pre>
2217  * @singleton
2218  */
2219 Roo.bootstrap.MessageBox = function(){
2220     var dlg, opt, mask, waitTimer;
2221     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2222     var buttons, activeTextEl, bwidth;
2223
2224     
2225     // private
2226     var handleButton = function(button){
2227         dlg.hide();
2228         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2229     };
2230
2231     // private
2232     var handleHide = function(){
2233         if(opt && opt.cls){
2234             dlg.el.removeClass(opt.cls);
2235         }
2236         //if(waitTimer){
2237         //    Roo.TaskMgr.stop(waitTimer);
2238         //    waitTimer = null;
2239         //}
2240     };
2241
2242     // private
2243     var updateButtons = function(b){
2244         var width = 0;
2245         if(!b){
2246             buttons["ok"].hide();
2247             buttons["cancel"].hide();
2248             buttons["yes"].hide();
2249             buttons["no"].hide();
2250             //dlg.footer.dom.style.display = 'none';
2251             return width;
2252         }
2253         dlg.footer.dom.style.display = '';
2254         for(var k in buttons){
2255             if(typeof buttons[k] != "function"){
2256                 if(b[k]){
2257                     buttons[k].show();
2258                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2259                     width += buttons[k].el.getWidth()+15;
2260                 }else{
2261                     buttons[k].hide();
2262                 }
2263             }
2264         }
2265         return width;
2266     };
2267
2268     // private
2269     var handleEsc = function(d, k, e){
2270         if(opt && opt.closable !== false){
2271             dlg.hide();
2272         }
2273         if(e){
2274             e.stopEvent();
2275         }
2276     };
2277
2278     return {
2279         /**
2280          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2281          * @return {Roo.BasicDialog} The BasicDialog element
2282          */
2283         getDialog : function(){
2284            if(!dlg){
2285                 dlg = new Roo.bootstrap.Modal( {
2286                     //draggable: true,
2287                     //resizable:false,
2288                     //constraintoviewport:false,
2289                     //fixedcenter:true,
2290                     //collapsible : false,
2291                     //shim:true,
2292                     //modal: true,
2293                   //  width:400,
2294                   //  height:100,
2295                     //buttonAlign:"center",
2296                     closeClick : function(){
2297                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2298                             handleButton("no");
2299                         }else{
2300                             handleButton("cancel");
2301                         }
2302                     }
2303                 });
2304                 dlg.render();
2305                 dlg.on("hide", handleHide);
2306                 mask = dlg.mask;
2307                 //dlg.addKeyListener(27, handleEsc);
2308                 buttons = {};
2309                 this.buttons = buttons;
2310                 var bt = this.buttonText;
2311                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2312                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2313                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2314                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2315                 Roo.log(buttons)
2316                 bodyEl = dlg.body.createChild({
2317
2318                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2319                         '<textarea class="roo-mb-textarea"></textarea>' +
2320                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2321                 });
2322                 msgEl = bodyEl.dom.firstChild;
2323                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2324                 textboxEl.enableDisplayMode();
2325                 textboxEl.addKeyListener([10,13], function(){
2326                     if(dlg.isVisible() && opt && opt.buttons){
2327                         if(opt.buttons.ok){
2328                             handleButton("ok");
2329                         }else if(opt.buttons.yes){
2330                             handleButton("yes");
2331                         }
2332                     }
2333                 });
2334                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2335                 textareaEl.enableDisplayMode();
2336                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2337                 progressEl.enableDisplayMode();
2338                 var pf = progressEl.dom.firstChild;
2339                 if (pf) {
2340                     pp = Roo.get(pf.firstChild);
2341                     pp.setHeight(pf.offsetHeight);
2342                 }
2343                 
2344             }
2345             return dlg;
2346         },
2347
2348         /**
2349          * Updates the message box body text
2350          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2351          * the XHTML-compliant non-breaking space character '&amp;#160;')
2352          * @return {Roo.MessageBox} This message box
2353          */
2354         updateText : function(text){
2355             if(!dlg.isVisible() && !opt.width){
2356                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2357             }
2358             msgEl.innerHTML = text || '&#160;';
2359       
2360             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2361             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2362             var w = Math.max(
2363                     Math.min(opt.width || cw , this.maxWidth), 
2364                     Math.max(opt.minWidth || this.minWidth, bwidth)
2365             );
2366             if(opt.prompt){
2367                 activeTextEl.setWidth(w);
2368             }
2369             if(dlg.isVisible()){
2370                 dlg.fixedcenter = false;
2371             }
2372             // to big, make it scroll. = But as usual stupid IE does not support
2373             // !important..
2374             
2375             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2376                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2377                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2378             } else {
2379                 bodyEl.dom.style.height = '';
2380                 bodyEl.dom.style.overflowY = '';
2381             }
2382             if (cw > w) {
2383                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2384             } else {
2385                 bodyEl.dom.style.overflowX = '';
2386             }
2387             
2388             dlg.setContentSize(w, bodyEl.getHeight());
2389             if(dlg.isVisible()){
2390                 dlg.fixedcenter = true;
2391             }
2392             return this;
2393         },
2394
2395         /**
2396          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2397          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2398          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2399          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2400          * @return {Roo.MessageBox} This message box
2401          */
2402         updateProgress : function(value, text){
2403             if(text){
2404                 this.updateText(text);
2405             }
2406             if (pp) { // weird bug on my firefox - for some reason this is not defined
2407                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2408             }
2409             return this;
2410         },        
2411
2412         /**
2413          * Returns true if the message box is currently displayed
2414          * @return {Boolean} True if the message box is visible, else false
2415          */
2416         isVisible : function(){
2417             return dlg && dlg.isVisible();  
2418         },
2419
2420         /**
2421          * Hides the message box if it is displayed
2422          */
2423         hide : function(){
2424             if(this.isVisible()){
2425                 dlg.hide();
2426             }  
2427         },
2428
2429         /**
2430          * Displays a new message box, or reinitializes an existing message box, based on the config options
2431          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2432          * The following config object properties are supported:
2433          * <pre>
2434 Property    Type             Description
2435 ----------  ---------------  ------------------------------------------------------------------------------------
2436 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2437                                    closes (defaults to undefined)
2438 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2439                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2440 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2441                                    progress and wait dialogs will ignore this property and always hide the
2442                                    close button as they can only be closed programmatically.
2443 cls               String           A custom CSS class to apply to the message box element
2444 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2445                                    displayed (defaults to 75)
2446 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2447                                    function will be btn (the name of the button that was clicked, if applicable,
2448                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2449                                    Progress and wait dialogs will ignore this option since they do not respond to
2450                                    user actions and can only be closed programmatically, so any required function
2451                                    should be called by the same code after it closes the dialog.
2452 icon              String           A CSS class that provides a background image to be used as an icon for
2453                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2454 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2455 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2456 modal             Boolean          False to allow user interaction with the page while the message box is
2457                                    displayed (defaults to true)
2458 msg               String           A string that will replace the existing message box body text (defaults
2459                                    to the XHTML-compliant non-breaking space character '&#160;')
2460 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2461 progress          Boolean          True to display a progress bar (defaults to false)
2462 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2463 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2464 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2465 title             String           The title text
2466 value             String           The string value to set into the active textbox element if displayed
2467 wait              Boolean          True to display a progress bar (defaults to false)
2468 width             Number           The width of the dialog in pixels
2469 </pre>
2470          *
2471          * Example usage:
2472          * <pre><code>
2473 Roo.Msg.show({
2474    title: 'Address',
2475    msg: 'Please enter your address:',
2476    width: 300,
2477    buttons: Roo.MessageBox.OKCANCEL,
2478    multiline: true,
2479    fn: saveAddress,
2480    animEl: 'addAddressBtn'
2481 });
2482 </code></pre>
2483          * @param {Object} config Configuration options
2484          * @return {Roo.MessageBox} This message box
2485          */
2486         show : function(options)
2487         {
2488             
2489             // this causes nightmares if you show one dialog after another
2490             // especially on callbacks..
2491              
2492             if(this.isVisible()){
2493                 
2494                 this.hide();
2495                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2496                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2497                 Roo.log("New Dialog Message:" +  options.msg )
2498                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2499                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2500                 
2501             }
2502             var d = this.getDialog();
2503             opt = options;
2504             d.setTitle(opt.title || "&#160;");
2505             d.close.setDisplayed(opt.closable !== false);
2506             activeTextEl = textboxEl;
2507             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2508             if(opt.prompt){
2509                 if(opt.multiline){
2510                     textboxEl.hide();
2511                     textareaEl.show();
2512                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2513                         opt.multiline : this.defaultTextHeight);
2514                     activeTextEl = textareaEl;
2515                 }else{
2516                     textboxEl.show();
2517                     textareaEl.hide();
2518                 }
2519             }else{
2520                 textboxEl.hide();
2521                 textareaEl.hide();
2522             }
2523             progressEl.setDisplayed(opt.progress === true);
2524             this.updateProgress(0);
2525             activeTextEl.dom.value = opt.value || "";
2526             if(opt.prompt){
2527                 dlg.setDefaultButton(activeTextEl);
2528             }else{
2529                 var bs = opt.buttons;
2530                 var db = null;
2531                 if(bs && bs.ok){
2532                     db = buttons["ok"];
2533                 }else if(bs && bs.yes){
2534                     db = buttons["yes"];
2535                 }
2536                 dlg.setDefaultButton(db);
2537             }
2538             bwidth = updateButtons(opt.buttons);
2539             this.updateText(opt.msg);
2540             if(opt.cls){
2541                 d.el.addClass(opt.cls);
2542             }
2543             d.proxyDrag = opt.proxyDrag === true;
2544             d.modal = opt.modal !== false;
2545             d.mask = opt.modal !== false ? mask : false;
2546             if(!d.isVisible()){
2547                 // force it to the end of the z-index stack so it gets a cursor in FF
2548                 document.body.appendChild(dlg.el.dom);
2549                 d.animateTarget = null;
2550                 d.show(options.animEl);
2551             }
2552             return this;
2553         },
2554
2555         /**
2556          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2557          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2558          * and closing the message box when the process is complete.
2559          * @param {String} title The title bar text
2560          * @param {String} msg The message box body text
2561          * @return {Roo.MessageBox} This message box
2562          */
2563         progress : function(title, msg){
2564             this.show({
2565                 title : title,
2566                 msg : msg,
2567                 buttons: false,
2568                 progress:true,
2569                 closable:false,
2570                 minWidth: this.minProgressWidth,
2571                 modal : true
2572             });
2573             return this;
2574         },
2575
2576         /**
2577          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2578          * If a callback function is passed it will be called after the user clicks the button, and the
2579          * id of the button that was clicked will be passed as the only parameter to the callback
2580          * (could also be the top-right close button).
2581          * @param {String} title The title bar text
2582          * @param {String} msg The message box body text
2583          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2584          * @param {Object} scope (optional) The scope of the callback function
2585          * @return {Roo.MessageBox} This message box
2586          */
2587         alert : function(title, msg, fn, scope){
2588             this.show({
2589                 title : title,
2590                 msg : msg,
2591                 buttons: this.OK,
2592                 fn: fn,
2593                 scope : scope,
2594                 modal : true
2595             });
2596             return this;
2597         },
2598
2599         /**
2600          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2601          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2602          * You are responsible for closing the message box when the process is complete.
2603          * @param {String} msg The message box body text
2604          * @param {String} title (optional) The title bar text
2605          * @return {Roo.MessageBox} This message box
2606          */
2607         wait : function(msg, title){
2608             this.show({
2609                 title : title,
2610                 msg : msg,
2611                 buttons: false,
2612                 closable:false,
2613                 progress:true,
2614                 modal:true,
2615                 width:300,
2616                 wait:true
2617             });
2618             waitTimer = Roo.TaskMgr.start({
2619                 run: function(i){
2620                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2621                 },
2622                 interval: 1000
2623             });
2624             return this;
2625         },
2626
2627         /**
2628          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2629          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2630          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2631          * @param {String} title The title bar text
2632          * @param {String} msg The message box body text
2633          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2634          * @param {Object} scope (optional) The scope of the callback function
2635          * @return {Roo.MessageBox} This message box
2636          */
2637         confirm : function(title, msg, fn, scope){
2638             this.show({
2639                 title : title,
2640                 msg : msg,
2641                 buttons: this.YESNO,
2642                 fn: fn,
2643                 scope : scope,
2644                 modal : true
2645             });
2646             return this;
2647         },
2648
2649         /**
2650          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2651          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2652          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2653          * (could also be the top-right close button) and the text that was entered will be passed as the two
2654          * parameters to the callback.
2655          * @param {String} title The title bar text
2656          * @param {String} msg The message box body text
2657          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2658          * @param {Object} scope (optional) The scope of the callback function
2659          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2660          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2661          * @return {Roo.MessageBox} This message box
2662          */
2663         prompt : function(title, msg, fn, scope, multiline){
2664             this.show({
2665                 title : title,
2666                 msg : msg,
2667                 buttons: this.OKCANCEL,
2668                 fn: fn,
2669                 minWidth:250,
2670                 scope : scope,
2671                 prompt:true,
2672                 multiline: multiline,
2673                 modal : true
2674             });
2675             return this;
2676         },
2677
2678         /**
2679          * Button config that displays a single OK button
2680          * @type Object
2681          */
2682         OK : {ok:true},
2683         /**
2684          * Button config that displays Yes and No buttons
2685          * @type Object
2686          */
2687         YESNO : {yes:true, no:true},
2688         /**
2689          * Button config that displays OK and Cancel buttons
2690          * @type Object
2691          */
2692         OKCANCEL : {ok:true, cancel:true},
2693         /**
2694          * Button config that displays Yes, No and Cancel buttons
2695          * @type Object
2696          */
2697         YESNOCANCEL : {yes:true, no:true, cancel:true},
2698
2699         /**
2700          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2701          * @type Number
2702          */
2703         defaultTextHeight : 75,
2704         /**
2705          * The maximum width in pixels of the message box (defaults to 600)
2706          * @type Number
2707          */
2708         maxWidth : 600,
2709         /**
2710          * The minimum width in pixels of the message box (defaults to 100)
2711          * @type Number
2712          */
2713         minWidth : 100,
2714         /**
2715          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2716          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2717          * @type Number
2718          */
2719         minProgressWidth : 250,
2720         /**
2721          * An object containing the default button text strings that can be overriden for localized language support.
2722          * Supported properties are: ok, cancel, yes and no.
2723          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2724          * @type Object
2725          */
2726         buttonText : {
2727             ok : "OK",
2728             cancel : "Cancel",
2729             yes : "Yes",
2730             no : "No"
2731         }
2732     };
2733 }();
2734
2735 /**
2736  * Shorthand for {@link Roo.MessageBox}
2737  */
2738 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2739 Roo.Msg = Roo.Msg || Roo.MessageBox;
2740 /*
2741  * - LGPL
2742  *
2743  * navbar
2744  * 
2745  */
2746
2747 /**
2748  * @class Roo.bootstrap.Navbar
2749  * @extends Roo.bootstrap.Component
2750  * Bootstrap Navbar class
2751
2752  * @constructor
2753  * Create a new Navbar
2754  * @param {Object} config The config object
2755  */
2756
2757
2758 Roo.bootstrap.Navbar = function(config){
2759     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2760     
2761 };
2762
2763 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2764     
2765     
2766    
2767     // private
2768     navItems : false,
2769     loadMask : false,
2770     
2771     
2772     getAutoCreate : function(){
2773         
2774         
2775         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2776         
2777     },
2778     
2779     initEvents :function ()
2780     {
2781         //Roo.log(this.el.select('.navbar-toggle',true));
2782         this.el.select('.navbar-toggle',true).on('click', function() {
2783            // Roo.log('click');
2784             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2785         }, this);
2786         
2787         var mark = {
2788             tag: "div",
2789             cls:"x-dlg-mask"
2790         }
2791         
2792         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2793         
2794         var size = this.el.getSize();
2795         this.maskEl.setSize(size.width, size.height);
2796         this.maskEl.enableDisplayMode("block");
2797         this.maskEl.hide();
2798         
2799         if(this.loadMask){
2800             this.maskEl.show();
2801         }
2802     },
2803     
2804     
2805     getChildContainer : function()
2806     {
2807         if (this.el.select('.collapse').getCount()) {
2808             return this.el.select('.collapse',true).first();
2809         }
2810         
2811         return this.el;
2812     },
2813     
2814     mask : function()
2815     {
2816         this.maskEl.show();
2817     },
2818     
2819     unmask : function()
2820     {
2821         this.maskEl.hide();
2822     }
2823     
2824     
2825     
2826 });
2827
2828
2829
2830  
2831
2832  /*
2833  * - LGPL
2834  *
2835  * navbar
2836  * 
2837  */
2838
2839 /**
2840  * @class Roo.bootstrap.NavSimplebar
2841  * @extends Roo.bootstrap.Navbar
2842  * Bootstrap Sidebar class
2843  *
2844  * @cfg {Boolean} inverse is inverted color
2845  * 
2846  * @cfg {String} type (nav | pills | tabs)
2847  * @cfg {Boolean} arrangement stacked | justified
2848  * @cfg {String} align (left | right) alignment
2849  * 
2850  * @cfg {Boolean} main (true|false) main nav bar? default false
2851  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2852  * 
2853  * @cfg {String} tag (header|footer|nav|div) default is nav 
2854
2855  * 
2856  * 
2857  * 
2858  * @constructor
2859  * Create a new Sidebar
2860  * @param {Object} config The config object
2861  */
2862
2863
2864 Roo.bootstrap.NavSimplebar = function(config){
2865     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2866 };
2867
2868 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2869     
2870     inverse: false,
2871     
2872     type: false,
2873     arrangement: '',
2874     align : false,
2875     
2876     
2877     
2878     main : false,
2879     
2880     
2881     tag : false,
2882     
2883     
2884     getAutoCreate : function(){
2885         
2886         
2887         var cfg = {
2888             tag : this.tag || 'div',
2889             cls : 'navbar'
2890         };
2891           
2892         
2893         cfg.cn = [
2894             {
2895                 cls: 'nav',
2896                 tag : 'ul'
2897             }
2898         ];
2899         
2900          
2901         this.type = this.type || 'nav';
2902         if (['tabs','pills'].indexOf(this.type)!==-1) {
2903             cfg.cn[0].cls += ' nav-' + this.type
2904         
2905         
2906         } else {
2907             if (this.type!=='nav') {
2908                 Roo.log('nav type must be nav/tabs/pills')
2909             }
2910             cfg.cn[0].cls += ' navbar-nav'
2911         }
2912         
2913         
2914         
2915         
2916         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2917             cfg.cn[0].cls += ' nav-' + this.arrangement;
2918         }
2919         
2920         
2921         if (this.align === 'right') {
2922             cfg.cn[0].cls += ' navbar-right';
2923         }
2924         
2925         if (this.inverse) {
2926             cfg.cls += ' navbar-inverse';
2927             
2928         }
2929         
2930         
2931         return cfg;
2932     
2933         
2934     }
2935     
2936     
2937     
2938 });
2939
2940
2941
2942  
2943
2944  
2945        /*
2946  * - LGPL
2947  *
2948  * navbar
2949  * 
2950  */
2951
2952 /**
2953  * @class Roo.bootstrap.NavHeaderbar
2954  * @extends Roo.bootstrap.NavSimplebar
2955  * Bootstrap Sidebar class
2956  *
2957  * @cfg {String} brand what is brand
2958  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2959  * @cfg {String} brand_href href of the brand
2960  * 
2961  * @constructor
2962  * Create a new Sidebar
2963  * @param {Object} config The config object
2964  */
2965
2966
2967 Roo.bootstrap.NavHeaderbar = function(config){
2968     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2969 };
2970
2971 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2972     
2973     position: '',
2974     brand: '',
2975     brand_href: false,
2976     
2977     
2978     getAutoCreate : function(){
2979         
2980         
2981         
2982         var   cfg = {
2983             tag: this.nav || 'nav',
2984             cls: 'navbar',
2985             role: 'navigation',
2986             cn: [
2987                 {
2988                     tag: 'div',
2989                     cls: 'navbar-header',
2990                     cn: [
2991                         {
2992                         tag: 'button',
2993                         type: 'button',
2994                         cls: 'navbar-toggle',
2995                         'data-toggle': 'collapse',
2996                         cn: [
2997                             {
2998                                 tag: 'span',
2999                                 cls: 'sr-only',
3000                                 html: 'Toggle navigation'
3001                             },
3002                             {
3003                                 tag: 'span',
3004                                 cls: 'icon-bar'
3005                             },
3006                             {
3007                                 tag: 'span',
3008                                 cls: 'icon-bar'
3009                             },
3010                             {
3011                                 tag: 'span',
3012                                 cls: 'icon-bar'
3013                             }
3014                         ]
3015                         }
3016                     ]
3017                 },
3018                 {
3019                 tag: 'div',
3020                 cls: 'collapse navbar-collapse'
3021                 }
3022             ]
3023         };
3024         
3025         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3026         
3027         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3028             cfg.cls += ' navbar-' + this.position;
3029             
3030             // tag can override this..
3031             
3032             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3033         }
3034         
3035         if (this.brand !== '') {
3036             cfg.cn[0].cn.push({
3037                 tag: 'a',
3038                 href: this.brand_href ? this.brand_href : '#',
3039                 cls: 'navbar-brand',
3040                 cn: [
3041                 this.brand
3042                 ]
3043             });
3044         }
3045         
3046         if(this.main){
3047             cfg.cls += ' main-nav';
3048         }
3049         
3050         
3051         return cfg;
3052
3053         
3054     }
3055     
3056     
3057     
3058 });
3059
3060
3061
3062  
3063
3064  /*
3065  * - LGPL
3066  *
3067  * navbar
3068  * 
3069  */
3070
3071 /**
3072  * @class Roo.bootstrap.NavSidebar
3073  * @extends Roo.bootstrap.Navbar
3074  * Bootstrap Sidebar class
3075  * 
3076  * @constructor
3077  * Create a new Sidebar
3078  * @param {Object} config The config object
3079  */
3080
3081
3082 Roo.bootstrap.NavSidebar = function(config){
3083     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3084 };
3085
3086 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3087     
3088     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3089     
3090     getAutoCreate : function(){
3091         
3092         
3093         return  {
3094             tag: 'div',
3095             cls: 'sidebar sidebar-nav'
3096         };
3097     
3098         
3099     }
3100     
3101     
3102     
3103 });
3104
3105
3106
3107  
3108
3109  /*
3110  * - LGPL
3111  *
3112  * nav group
3113  * 
3114  */
3115
3116 /**
3117  * @class Roo.bootstrap.NavGroup
3118  * @extends Roo.bootstrap.Component
3119  * Bootstrap NavGroup class
3120  * @cfg {String} align left | right
3121  * @cfg {Boolean} inverse false | true
3122  * @cfg {String} type (nav|pills|tab) default nav
3123  * @cfg {String} navId - reference Id for navbar.
3124
3125  * 
3126  * @constructor
3127  * Create a new nav group
3128  * @param {Object} config The config object
3129  */
3130
3131 Roo.bootstrap.NavGroup = function(config){
3132     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3133     this.navItems = [];
3134     Roo.bootstrap.NavGroup.register(this);
3135      this.addEvents({
3136         /**
3137              * @event changed
3138              * Fires when the active item changes
3139              * @param {Roo.bootstrap.NavGroup} this
3140              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3141              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3142          */
3143         'changed': true
3144      });
3145     
3146 };
3147
3148 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3149     
3150     align: '',
3151     inverse: false,
3152     form: false,
3153     type: 'nav',
3154     navId : '',
3155     // private
3156     
3157     navItems : false,
3158     
3159     getAutoCreate : function()
3160     {
3161         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3162         
3163         cfg = {
3164             tag : 'ul',
3165             cls: 'nav' 
3166         }
3167         
3168         if (['tabs','pills'].indexOf(this.type)!==-1) {
3169             cfg.cls += ' nav-' + this.type
3170         } else {
3171             if (this.type!=='nav') {
3172                 Roo.log('nav type must be nav/tabs/pills')
3173             }
3174             cfg.cls += ' navbar-nav'
3175         }
3176         
3177         if (this.parent().sidebar) {
3178             cfg = {
3179                 tag: 'ul',
3180                 cls: 'dashboard-menu sidebar-menu'
3181             }
3182             
3183             return cfg;
3184         }
3185         
3186         if (this.form === true) {
3187             cfg = {
3188                 tag: 'form',
3189                 cls: 'navbar-form'
3190             }
3191             
3192             if (this.align === 'right') {
3193                 cfg.cls += ' navbar-right';
3194             } else {
3195                 cfg.cls += ' navbar-left';
3196             }
3197         }
3198         
3199         if (this.align === 'right') {
3200             cfg.cls += ' navbar-right';
3201         }
3202         
3203         if (this.inverse) {
3204             cfg.cls += ' navbar-inverse';
3205             
3206         }
3207         
3208         
3209         return cfg;
3210     },
3211     
3212     setActiveItem : function(item)
3213     {
3214         var prev = false;
3215         Roo.each(this.navItems, function(v){
3216             if (v == item) {
3217                 return ;
3218             }
3219             if (v.isActive()) {
3220                 v.setActive(false, true);
3221                 prev = v;
3222                 
3223             }
3224             
3225         });
3226
3227         item.setActive(true, true);
3228         this.fireEvent('changed', this, item, prev);
3229         
3230         
3231     },
3232     
3233     
3234     register : function(item)
3235     {
3236         this.navItems.push( item);
3237         item.navId = this.navId;
3238     
3239     },
3240     getNavItem: function(tabId)
3241     {
3242         var ret = false;
3243         Roo.each(this.navItems, function(e) {
3244             if (e.tabId == tabId) {
3245                ret =  e;
3246                return false;
3247             }
3248             return true;
3249             
3250         });
3251         return ret;
3252     }
3253 });
3254
3255  
3256 Roo.apply(Roo.bootstrap.NavGroup, {
3257     
3258     groups: {},
3259     
3260     register : function(navgrp)
3261     {
3262         this.groups[navgrp.navId] = navgrp;
3263         
3264     },
3265     get: function(navId) {
3266         return this.groups[navId];
3267     }
3268     
3269     
3270     
3271 });
3272
3273  /*
3274  * - LGPL
3275  *
3276  * row
3277  * 
3278  */
3279
3280 /**
3281  * @class Roo.bootstrap.Navbar.Item
3282  * @extends Roo.bootstrap.Component
3283  * Bootstrap Navbar.Button class
3284  * @cfg {String} href  link to
3285  * @cfg {String} html content of button
3286  * @cfg {String} badge text inside badge
3287  * @cfg {String} glyphicon name of glyphicon
3288  * @cfg {String} icon name of font awesome icon
3289  * @cfg {Boolean} active Is item active
3290  * @cfg {Boolean} preventDefault (true | false) default false
3291  * @cfg {String} tabId the tab that this item activates.
3292   
3293  * @constructor
3294  * Create a new Navbar Button
3295  * @param {Object} config The config object
3296  */
3297 Roo.bootstrap.Navbar.Item = function(config){
3298     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3299     this.addEvents({
3300         // raw events
3301         /**
3302          * @event click
3303          * The raw click event for the entire grid.
3304          * @param {Roo.EventObject} e
3305          */
3306         "click" : true,
3307          /**
3308             * @event changed
3309             * Fires when the active item active state changes
3310             * @param {Roo.bootstrap.Navbar.Item} this
3311             * @param {boolean} state the new state
3312              
3313          */
3314         'changed': true
3315     });
3316    
3317 };
3318
3319 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3320     
3321     href: false,
3322     html: '',
3323     badge: '',
3324     icon: false,
3325     glyphicon: false,
3326     active: false,
3327     preventDefault : false,
3328     tabId : false,
3329     
3330     getAutoCreate : function(){
3331         
3332         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3333         
3334         if (this.parent().parent().sidebar === true) {
3335             cfg = {
3336                 tag: 'li',
3337                 cls: '',
3338                 cn: [
3339                     {
3340                     tag: 'p',
3341                     cls: ''
3342                     }
3343                 ]
3344             }
3345             
3346             if (this.html) {
3347                 cfg.cn[0].html = this.html;
3348             }
3349             
3350             if (this.active) {
3351                 this.cls += ' active';
3352             }
3353             
3354             if (this.menu) {
3355                 cfg.cn[0].cls += ' dropdown-toggle';
3356                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3357             }
3358             
3359             if (this.href) {
3360                 cfg.cn[0].tag = 'a',
3361                 cfg.cn[0].href = this.href;
3362             }
3363             
3364             if (this.glyphicon) {
3365                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3366             }
3367                 
3368             if (this.icon) {
3369                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3370             }
3371             
3372             return cfg;
3373         }
3374         
3375         cfg = {
3376             tag: 'li',
3377                 cls: 'nav-item'
3378         }
3379             
3380         if (this.active) {
3381             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3382         }
3383             
3384         cfg.cn = [
3385             {
3386                 tag: 'p',
3387                 html: 'Text'
3388             }
3389         ];
3390         
3391         if (this.glyphicon) {
3392             if(cfg.html){cfg.html = ' ' + this.html};
3393             cfg.cn=[
3394                 {
3395                     tag: 'span',
3396                     cls: 'glyphicon glyphicon-' + this.glyphicon
3397                 }
3398             ];
3399         }
3400         
3401         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3402         
3403         if (this.menu) {
3404             cfg.cn[0].tag='a';
3405             cfg.cn[0].href='#';
3406             cfg.cn[0].html += " <span class='caret'></span>";
3407         //}else if (!this.href) {
3408         //    cfg.cn[0].tag='p';
3409         //    cfg.cn[0].cls='navbar-text';
3410         } else {
3411             cfg.cn[0].tag='a';
3412             cfg.cn[0].href=this.href||'#';
3413             cfg.cn[0].html=this.html;
3414         }
3415         
3416         if (this.badge !== '') {
3417             
3418             cfg.cn[0].cn=[
3419             cfg.cn[0].html + ' ',
3420             {
3421                 tag: 'span',
3422                 cls: 'badge',
3423                 html: this.badge
3424             }
3425             ];
3426             cfg.cn[0].html=''
3427         }
3428          
3429         if (this.icon) {
3430             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3431         }
3432         
3433         return cfg;
3434     },
3435     initEvents: function() {
3436        // Roo.log('init events?');
3437        // Roo.log(this.el.dom);
3438         this.el.select('a',true).on('click', this.onClick, this);
3439         // at this point parent should be available..
3440         this.parent().register(this);
3441     },
3442     
3443     onClick : function(e)
3444     {
3445         if(this.preventDefault){
3446             e.preventDefault();
3447         }
3448         
3449         if(this.fireEvent('click', this, e) === false){
3450             return;
3451         };
3452         
3453         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3454              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3455                 this.parent().setActiveItem(this);
3456             }
3457             
3458             
3459             
3460         } 
3461     },
3462     
3463     isActive: function () {
3464         return this.active
3465     },
3466     setActive : function(state, fire)
3467     {
3468         this.active = state;
3469         if (!state ) {
3470             this.el.removeClass('active');
3471         } else if (!this.el.hasClass('active')) {
3472             this.el.addClass('active');
3473         }
3474         if (fire) {
3475             this.fireEvent('changed', this, state);
3476         }
3477         
3478         
3479     }
3480      // this should not be here...
3481  
3482 });
3483  
3484
3485  /*
3486  * - LGPL
3487  *
3488  * row
3489  * 
3490  */
3491
3492 /**
3493  * @class Roo.bootstrap.NavItem
3494  * @extends Roo.bootstrap.Component
3495  * Bootstrap Navbar.NavItem class
3496  * @cfg {String} href  link to
3497  * @cfg {String} html content of button
3498  * @cfg {String} badge text inside badge
3499  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3500  * @cfg {String} glyphicon name of glyphicon
3501  * @cfg {String} icon name of font awesome icon
3502  * @cfg {Boolean} active Is item active
3503  * @cfg {Boolean} preventDefault (true | false) default false
3504  * @cfg {String} tabId the tab that this item activates.
3505   
3506  * @constructor
3507  * Create a new Navbar Item
3508  * @param {Object} config The config object
3509  */
3510 Roo.bootstrap.NavItem = function(config){
3511     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3512     this.addEvents({
3513         // raw events
3514         /**
3515          * @event click
3516          * The raw click event for the entire grid.
3517          * @param {Roo.EventObject} e
3518          */
3519         "click" : true,
3520          /**
3521             * @event changed
3522             * Fires when the active item active state changes
3523             * @param {Roo.bootstrap.NavItem} this
3524             * @param {boolean} state the new state
3525              
3526          */
3527         'changed': true
3528     });
3529    
3530 };
3531
3532 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3533     
3534     href: false,
3535     html: '',
3536     badge: '',
3537     icon: false,
3538     glyphicon: false,
3539     active: false,
3540     preventDefault : false,
3541     tabId : false,
3542     
3543     getAutoCreate : function(){
3544          
3545         var cfg = {
3546             tag: 'li',
3547             cls: 'nav-item',
3548             cn : [
3549                 {
3550                     tag: 'a',
3551                     href : this.href || "#",
3552                     html: this.html || ''
3553                 }
3554             ]
3555         }
3556             
3557         if (this.active) {
3558             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3559         }
3560             
3561         // glyphicon and icon go before content..
3562         if (this.glyphicon || this.icon) {
3563              if (this.icon) {
3564                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html + '</span>'
3565             } else {
3566                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span>'  + cfg.cn[0].html;
3567             }
3568         }
3569         
3570         
3571         
3572         if (this.menu) {
3573             
3574             cfg.cn[0].html += " <span class='caret'></span>";
3575          
3576         }
3577         
3578         if (this.badge !== '') {
3579              
3580             cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3581         }
3582         
3583         
3584         
3585         return cfg;
3586     },
3587     initEvents: function() {
3588        // Roo.log('init events?');
3589        // Roo.log(this.el.dom);
3590        if (typeof (this.menu) != 'undefined') {
3591             this.menu.parentType = this.xtype;
3592             this.menu.triggerEl = this.el;
3593             this.addxtype(Roo.apply({}, this.menu));
3594         }
3595
3596        
3597         this.el.select('a',true).on('click', this.onClick, this);
3598         // at this point parent should be available..
3599         this.parent().register(this);
3600     },
3601     
3602     onClick : function(e)
3603     {
3604         if(this.preventDefault){
3605             e.preventDefault();
3606         }
3607         
3608         if(this.fireEvent('click', this, e) === false){
3609             return;
3610         };
3611         
3612         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3613              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3614                 this.parent().setActiveItem(this);
3615             }
3616             
3617             
3618             
3619         } 
3620     },
3621     
3622     isActive: function () {
3623         return this.active
3624     },
3625     setActive : function(state, fire)
3626     {
3627         this.active = state;
3628         if (!state ) {
3629             this.el.removeClass('active');
3630         } else if (!this.el.hasClass('active')) {
3631             this.el.addClass('active');
3632         }
3633         if (fire) {
3634             this.fireEvent('changed', this, state);
3635         }
3636         
3637         
3638     }
3639      // this should not be here...
3640  
3641 });
3642  
3643
3644  /*
3645  * - LGPL
3646  *
3647  * sidebar item
3648  *
3649  *  li
3650  *    <span> icon </span>
3651  *    <span> text </span>
3652  *    <span>badge </span>
3653  */
3654
3655 /**
3656  * @class Roo.bootstrap.NavSidebarItem
3657  * @extends Roo.bootstrap.Component
3658  * Bootstrap Navbar.NavSidebarItem class
3659  * @constructor
3660  * Create a new Navbar Button
3661  * @param {Object} config The config object
3662  */
3663 Roo.bootstrap.NavSidebarItem = function(config){
3664     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3665     this.addEvents({
3666         // raw events
3667         /**
3668          * @event click
3669          * The raw click event for the entire grid.
3670          * @param {Roo.EventObject} e
3671          */
3672         "click" : true,
3673          /**
3674             * @event changed
3675             * Fires when the active item active state changes
3676             * @param {Roo.bootstrap.Navbar.Item} this
3677             * @param {boolean} state the new state
3678              
3679          */
3680         'changed': true
3681     });
3682    
3683 };
3684
3685 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3686     
3687     
3688     getAutoCreate : function(){
3689         
3690         
3691         var a = {
3692                 tag: 'a',
3693                 href : this.href || '#',
3694                 cls: '',
3695                 html : '',
3696                 cn : []
3697         };
3698         var cfg = {
3699             tag: 'li',
3700             cls: '',
3701             cn: [ a ]
3702         }
3703         var span = {
3704             tag: 'span',
3705             html : this.html || ''
3706         }
3707         
3708         
3709         if (this.active) {
3710             cfg.cls += ' active';
3711         }
3712         
3713         // left icon..
3714         if (this.glyphicon || this.icon) {
3715             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3716             a.cn.push({ tag : 'i', cls : c }) ;
3717         }
3718         // html..
3719         a.cn.push(span);
3720         // then badge..
3721         if (this.badge !== '') {
3722             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3723         }
3724         // fi
3725         if (this.menu) {
3726             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3727             a.cls += 'dropdown-toggle treeview' ;
3728             
3729         }
3730         
3731         
3732         
3733         return cfg;
3734          
3735            
3736     }
3737    
3738      
3739  
3740 });
3741  
3742
3743  /*
3744  * - LGPL
3745  *
3746  * row
3747  * 
3748  */
3749
3750 /**
3751  * @class Roo.bootstrap.Row
3752  * @extends Roo.bootstrap.Component
3753  * Bootstrap Row class (contains columns...)
3754  * 
3755  * @constructor
3756  * Create a new Row
3757  * @param {Object} config The config object
3758  */
3759
3760 Roo.bootstrap.Row = function(config){
3761     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3762 };
3763
3764 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3765     
3766     getAutoCreate : function(){
3767        return {
3768             cls: 'row clearfix'
3769        };
3770     }
3771     
3772     
3773 });
3774
3775  
3776
3777  /*
3778  * - LGPL
3779  *
3780  * element
3781  * 
3782  */
3783
3784 /**
3785  * @class Roo.bootstrap.Element
3786  * @extends Roo.bootstrap.Component
3787  * Bootstrap Element class
3788  * @cfg {String} html contents of the element
3789  * @cfg {String} tag tag of the element
3790  * @cfg {String} cls class of the element
3791  * 
3792  * @constructor
3793  * Create a new Element
3794  * @param {Object} config The config object
3795  */
3796
3797 Roo.bootstrap.Element = function(config){
3798     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3799 };
3800
3801 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3802     
3803     tag: 'div',
3804     cls: '',
3805     html: '',
3806      
3807     
3808     getAutoCreate : function(){
3809         
3810         var cfg = {
3811             tag: this.tag,
3812             cls: this.cls,
3813             html: this.html
3814         }
3815         
3816         
3817         
3818         return cfg;
3819     }
3820    
3821 });
3822
3823  
3824
3825  /*
3826  * - LGPL
3827  *
3828  * pagination
3829  * 
3830  */
3831
3832 /**
3833  * @class Roo.bootstrap.Pagination
3834  * @extends Roo.bootstrap.Component
3835  * Bootstrap Pagination class
3836  * @cfg {String} size xs | sm | md | lg
3837  * @cfg {Boolean} inverse false | true
3838  * 
3839  * @constructor
3840  * Create a new Pagination
3841  * @param {Object} config The config object
3842  */
3843
3844 Roo.bootstrap.Pagination = function(config){
3845     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3846 };
3847
3848 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3849     
3850     cls: false,
3851     size: false,
3852     inverse: false,
3853     
3854     getAutoCreate : function(){
3855         var cfg = {
3856             tag: 'ul',
3857                 cls: 'pagination'
3858         };
3859         if (this.inverse) {
3860             cfg.cls += ' inverse';
3861         }
3862         if (this.html) {
3863             cfg.html=this.html;
3864         }
3865         if (this.cls) {
3866             cfg.cls += " " + this.cls;
3867         }
3868         return cfg;
3869     }
3870    
3871 });
3872
3873  
3874
3875  /*
3876  * - LGPL
3877  *
3878  * Pagination item
3879  * 
3880  */
3881
3882
3883 /**
3884  * @class Roo.bootstrap.PaginationItem
3885  * @extends Roo.bootstrap.Component
3886  * Bootstrap PaginationItem class
3887  * @cfg {String} html text
3888  * @cfg {String} href the link
3889  * @cfg {Boolean} preventDefault (true | false) default true
3890  * @cfg {Boolean} active (true | false) default false
3891  * 
3892  * 
3893  * @constructor
3894  * Create a new PaginationItem
3895  * @param {Object} config The config object
3896  */
3897
3898
3899 Roo.bootstrap.PaginationItem = function(config){
3900     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3901     this.addEvents({
3902         // raw events
3903         /**
3904          * @event click
3905          * The raw click event for the entire grid.
3906          * @param {Roo.EventObject} e
3907          */
3908         "click" : true
3909     });
3910 };
3911
3912 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3913     
3914     href : false,
3915     html : false,
3916     preventDefault: true,
3917     active : false,
3918     cls : false,
3919     
3920     getAutoCreate : function(){
3921         var cfg= {
3922             tag: 'li',
3923             cn: [
3924                 {
3925                     tag : 'a',
3926                     href : this.href ? this.href : '#',
3927                     html : this.html ? this.html : ''
3928                 }
3929             ]
3930         };
3931         
3932         if(this.cls){
3933             cfg.cls = this.cls;
3934         }
3935         
3936         if(this.active){
3937             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3938         }
3939         
3940         return cfg;
3941     },
3942     
3943     initEvents: function() {
3944         
3945         this.el.on('click', this.onClick, this);
3946         
3947     },
3948     onClick : function(e)
3949     {
3950         Roo.log('PaginationItem on click ');
3951         if(this.preventDefault){
3952             e.preventDefault();
3953         }
3954         
3955         this.fireEvent('click', this, e);
3956     }
3957    
3958 });
3959
3960  
3961
3962  /*
3963  * - LGPL
3964  *
3965  * slider
3966  * 
3967  */
3968
3969
3970 /**
3971  * @class Roo.bootstrap.Slider
3972  * @extends Roo.bootstrap.Component
3973  * Bootstrap Slider class
3974  *    
3975  * @constructor
3976  * Create a new Slider
3977  * @param {Object} config The config object
3978  */
3979
3980 Roo.bootstrap.Slider = function(config){
3981     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3982 };
3983
3984 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3985     
3986     getAutoCreate : function(){
3987         
3988         var cfg = {
3989             tag: 'div',
3990             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3991             cn: [
3992                 {
3993                     tag: 'a',
3994                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3995                 }
3996             ]
3997         }
3998         
3999         return cfg;
4000     }
4001    
4002 });
4003
4004  /*
4005  * - LGPL
4006  *
4007  * table
4008  * 
4009  */
4010
4011 /**
4012  * @class Roo.bootstrap.Table
4013  * @extends Roo.bootstrap.Component
4014  * Bootstrap Table class
4015  * @cfg {String} cls table class
4016  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4017  * @cfg {String} bgcolor Specifies the background color for a table
4018  * @cfg {Number} border Specifies whether the table cells should have borders or not
4019  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4020  * @cfg {Number} cellspacing Specifies the space between cells
4021  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4022  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4023  * @cfg {String} sortable Specifies that the table should be sortable
4024  * @cfg {String} summary Specifies a summary of the content of a table
4025  * @cfg {Number} width Specifies the width of a table
4026  * 
4027  * @cfg {boolean} striped Should the rows be alternative striped
4028  * @cfg {boolean} bordered Add borders to the table
4029  * @cfg {boolean} hover Add hover highlighting
4030  * @cfg {boolean} condensed Format condensed
4031  * @cfg {boolean} responsive Format condensed
4032  *
4033  
4034  
4035  * 
4036  * @constructor
4037  * Create a new Table
4038  * @param {Object} config The config object
4039  */
4040
4041 Roo.bootstrap.Table = function(config){
4042     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4043     
4044     if (this.sm) {
4045         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4046         this.sm = this.selModel;
4047         this.sm.xmodule = this.xmodule || false;
4048     }
4049     if (this.cm && typeof(this.cm.config) == 'undefined') {
4050         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
4051         this.cm = this.colModel;
4052         this.cm.xmodule = this.xmodule || false;
4053     }
4054     if (this.store) {
4055         this.store= Roo.factory(this.store, Roo.data);
4056         this.ds = this.store;
4057         this.ds.xmodule = this.xmodule || false;
4058          
4059     }
4060 };
4061
4062 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4063     
4064     cls: false,
4065     align: false,
4066     bgcolor: false,
4067     border: false,
4068     cellpadding: false,
4069     cellspacing: false,
4070     frame: false,
4071     rules: false,
4072     sortable: false,
4073     summary: false,
4074     width: false,
4075     striped : false,
4076     bordered: false,
4077     hover:  false,
4078     condensed : false,
4079     responsive : false,
4080     sm : false,
4081     cm : false,
4082     store : false,
4083     
4084     getAutoCreate : function(){
4085         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4086         
4087         cfg = {
4088             tag: 'table',
4089             cls : 'table',
4090             cn : []
4091         }
4092             
4093         if (this.striped) {
4094             cfg.cls += ' table-striped';
4095         }
4096         if (this.hover) {
4097             cfg.cls += ' table-hover';
4098         }
4099         if (this.bordered) {
4100             cfg.cls += ' table-bordered';
4101         }
4102         if (this.condensed) {
4103             cfg.cls += ' table-condensed';
4104         }
4105         if (this.responsive) {
4106             cfg.cls += ' table-responsive';
4107         }
4108         
4109           
4110         
4111         
4112         if (this.cls) {
4113             cfg.cls+=  ' ' +this.cls;
4114         }
4115         
4116         // this lot should be simplifed...
4117         
4118         if (this.align) {
4119             cfg.align=this.align;
4120         }
4121         if (this.bgcolor) {
4122             cfg.bgcolor=this.bgcolor;
4123         }
4124         if (this.border) {
4125             cfg.border=this.border;
4126         }
4127         if (this.cellpadding) {
4128             cfg.cellpadding=this.cellpadding;
4129         }
4130         if (this.cellspacing) {
4131             cfg.cellspacing=this.cellspacing;
4132         }
4133         if (this.frame) {
4134             cfg.frame=this.frame;
4135         }
4136         if (this.rules) {
4137             cfg.rules=this.rules;
4138         }
4139         if (this.sortable) {
4140             cfg.sortable=this.sortable;
4141         }
4142         if (this.summary) {
4143             cfg.summary=this.summary;
4144         }
4145         if (this.width) {
4146             cfg.width=this.width;
4147         }
4148         
4149         if(this.store || this.cm){
4150             cfg.cn.push(this.renderHeader());
4151             cfg.cn.push(this.renderBody());
4152             cfg.cn.push(this.renderFooter());
4153             
4154             cfg.cls+=  ' TableGrid';
4155         }
4156         
4157         return cfg;
4158     },
4159 //    
4160 //    initTableGrid : function()
4161 //    {
4162 //        var cfg = {};
4163 //        
4164 //        var header = {
4165 //            tag: 'thead',
4166 //            cn : []
4167 //        };
4168 //        
4169 //        var cm = this.cm;
4170 //        
4171 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4172 //            header.cn.push({
4173 //                tag: 'th',
4174 //                html: cm.getColumnHeader(i)
4175 //            })
4176 //        }
4177 //        
4178 //        cfg.push(header);
4179 //        
4180 //        return cfg;
4181 //        
4182 //        
4183 //    },
4184     
4185     initEvents : function()
4186     {   
4187         if(!this.store || !this.cm){
4188             return;
4189         }
4190         
4191         Roo.log('initEvents with ds!!!!');
4192         
4193         var _this = this;
4194         
4195         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4196             e.on('click', _this.sort, _this);
4197         });
4198 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4199 //        this.maskEl.enableDisplayMode("block");
4200 //        this.maskEl.show();
4201         
4202         this.store.on('load', this.onLoad, this);
4203         this.store.on('beforeload', this.onBeforeLoad, this);
4204         
4205         this.store.load();
4206         
4207         
4208         
4209     },
4210     
4211     sort : function(e,el)
4212     {
4213         var col = Roo.get(el)
4214         
4215         if(!col.hasClass('sortable')){
4216             return;
4217         }
4218         
4219         var sort = col.attr('sort');
4220         var dir = 'ASC';
4221         
4222         if(col.hasClass('glyphicon-arrow-up')){
4223             dir = 'DESC';
4224         }
4225         
4226         this.store.sortInfo = {field : sort, direction : dir};
4227         
4228         this.store.load();
4229     },
4230     
4231     renderHeader : function()
4232     {
4233         var header = {
4234             tag: 'thead',
4235             cn : []
4236         };
4237         
4238         var cm = this.cm;
4239         
4240         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4241             
4242             var config = cm.config[i];
4243             
4244             var c = {
4245                 tag: 'th',
4246                 html: cm.getColumnHeader(i)
4247             };
4248             
4249             if(typeof(config.dataIndex) != 'undefined'){
4250                 c.sort = config.dataIndex;
4251             }
4252             
4253             if(typeof(config.sortable) != 'undefined' && config.sortable){
4254                 c.cls = 'sortable';
4255             }
4256             
4257             if(typeof(config.width) != 'undefined'){
4258                 c.style = 'width:' + config.width + 'px';
4259             }
4260             
4261             header.cn.push(c)
4262         }
4263         
4264         return header;
4265     },
4266     
4267     renderBody : function()
4268     {
4269         var body = {
4270             tag: 'tbody',
4271             cn : []
4272         };
4273         
4274         return body;
4275     },
4276     
4277     renderFooter : function()
4278     {
4279         var footer = {
4280             tag: 'tfoot',
4281             cn : []
4282         };
4283         
4284         return footer;
4285     },
4286     
4287     onLoad : function()
4288     {
4289         Roo.log('ds onload');
4290         
4291         var _this = this;
4292         var cm = this.cm;
4293         
4294         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4295             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4296             
4297             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4298                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4299             }
4300             
4301             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4302                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4303             }
4304         });
4305         
4306         var tbody = this.el.select('tbody', true).first();
4307         
4308         var renders = [];
4309         
4310         if(this.store.getCount() > 0){
4311             this.store.data.each(function(d){
4312                 var row = {
4313                     tag : 'tr',
4314                     cn : []
4315                 };
4316                 
4317                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4318                     var renderer = cm.getRenderer(i);
4319                     var config = cm.config[i];
4320                     var value = '';
4321                     var id = Roo.id();
4322                     
4323                     if(typeof(renderer) !== 'undefined'){
4324                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4325                     }
4326                     
4327                     if(typeof(value) === 'object'){
4328                         renders.push({
4329                             id : id,
4330                             cfg : value 
4331                         })
4332                     }
4333                     
4334                     var td = {
4335                         tag: 'td',
4336                         id: id,
4337                         html: (typeof(value) === 'object') ? '' : value
4338                     };
4339                     
4340                     if(typeof(config.width) != 'undefined'){
4341                         td.style = 'width:' +  config.width + 'px';
4342                     }
4343                     
4344                     row.cn.push(td);
4345                    
4346                 }
4347                 
4348                 tbody.createChild(row);
4349                 
4350             });
4351         }
4352         
4353         
4354         if(renders.length){
4355             var _this = this;
4356             Roo.each(renders, function(r){
4357                 _this.renderColumn(r);
4358             })
4359         }
4360 //        
4361 //        if(this.loadMask){
4362 //            this.maskEl.hide();
4363 //        }
4364     },
4365     
4366     onBeforeLoad : function()
4367     {
4368         Roo.log('ds onBeforeLoad');
4369         
4370         this.clear();
4371         
4372 //        if(this.loadMask){
4373 //            this.maskEl.show();
4374 //        }
4375     },
4376     
4377     clear : function()
4378     {
4379         this.el.select('tbody', true).first().dom.innerHTML = '';
4380     },
4381     
4382     getSelectionModel : function(){
4383         if(!this.selModel){
4384             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4385         }
4386         return this.selModel;
4387     },
4388     
4389     renderColumn : function(r)
4390     {
4391         var _this = this;
4392         r.cfg.render(Roo.get(r.id));
4393         
4394         if(r.cfg.cn){
4395             Roo.each(r.cfg.cn, function(c){
4396                 var child = {
4397                     id: r.id,
4398                     cfg: c
4399                 }
4400                 _this.renderColumn(child);
4401             })
4402         }
4403     }
4404    
4405 });
4406
4407  
4408
4409  /*
4410  * - LGPL
4411  *
4412  * table cell
4413  * 
4414  */
4415
4416 /**
4417  * @class Roo.bootstrap.TableCell
4418  * @extends Roo.bootstrap.Component
4419  * Bootstrap TableCell class
4420  * @cfg {String} html cell contain text
4421  * @cfg {String} cls cell class
4422  * @cfg {String} tag cell tag (td|th) default td
4423  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4424  * @cfg {String} align Aligns the content in a cell
4425  * @cfg {String} axis Categorizes cells
4426  * @cfg {String} bgcolor Specifies the background color of a cell
4427  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4428  * @cfg {Number} colspan Specifies the number of columns a cell should span
4429  * @cfg {String} headers Specifies one or more header cells a cell is related to
4430  * @cfg {Number} height Sets the height of a cell
4431  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4432  * @cfg {Number} rowspan Sets the number of rows a cell should span
4433  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4434  * @cfg {String} valign Vertical aligns the content in a cell
4435  * @cfg {Number} width Specifies the width of a cell
4436  * 
4437  * @constructor
4438  * Create a new TableCell
4439  * @param {Object} config The config object
4440  */
4441
4442 Roo.bootstrap.TableCell = function(config){
4443     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4444 };
4445
4446 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4447     
4448     html: false,
4449     cls: false,
4450     tag: false,
4451     abbr: false,
4452     align: false,
4453     axis: false,
4454     bgcolor: false,
4455     charoff: false,
4456     colspan: false,
4457     headers: false,
4458     height: false,
4459     nowrap: false,
4460     rowspan: false,
4461     scope: false,
4462     valign: false,
4463     width: false,
4464     
4465     
4466     getAutoCreate : function(){
4467         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4468         
4469         cfg = {
4470             tag: 'td'
4471         }
4472         
4473         if(this.tag){
4474             cfg.tag = this.tag;
4475         }
4476         
4477         if (this.html) {
4478             cfg.html=this.html
4479         }
4480         if (this.cls) {
4481             cfg.cls=this.cls
4482         }
4483         if (this.abbr) {
4484             cfg.abbr=this.abbr
4485         }
4486         if (this.align) {
4487             cfg.align=this.align
4488         }
4489         if (this.axis) {
4490             cfg.axis=this.axis
4491         }
4492         if (this.bgcolor) {
4493             cfg.bgcolor=this.bgcolor
4494         }
4495         if (this.charoff) {
4496             cfg.charoff=this.charoff
4497         }
4498         if (this.colspan) {
4499             cfg.colspan=this.colspan
4500         }
4501         if (this.headers) {
4502             cfg.headers=this.headers
4503         }
4504         if (this.height) {
4505             cfg.height=this.height
4506         }
4507         if (this.nowrap) {
4508             cfg.nowrap=this.nowrap
4509         }
4510         if (this.rowspan) {
4511             cfg.rowspan=this.rowspan
4512         }
4513         if (this.scope) {
4514             cfg.scope=this.scope
4515         }
4516         if (this.valign) {
4517             cfg.valign=this.valign
4518         }
4519         if (this.width) {
4520             cfg.width=this.width
4521         }
4522         
4523         
4524         return cfg;
4525     }
4526    
4527 });
4528
4529  
4530
4531  /*
4532  * - LGPL
4533  *
4534  * table row
4535  * 
4536  */
4537
4538 /**
4539  * @class Roo.bootstrap.TableRow
4540  * @extends Roo.bootstrap.Component
4541  * Bootstrap TableRow class
4542  * @cfg {String} cls row class
4543  * @cfg {String} align Aligns the content in a table row
4544  * @cfg {String} bgcolor Specifies a background color for a table row
4545  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4546  * @cfg {String} valign Vertical aligns the content in a table row
4547  * 
4548  * @constructor
4549  * Create a new TableRow
4550  * @param {Object} config The config object
4551  */
4552
4553 Roo.bootstrap.TableRow = function(config){
4554     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4555 };
4556
4557 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4558     
4559     cls: false,
4560     align: false,
4561     bgcolor: false,
4562     charoff: false,
4563     valign: false,
4564     
4565     getAutoCreate : function(){
4566         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4567         
4568         cfg = {
4569             tag: 'tr'
4570         }
4571             
4572         if(this.cls){
4573             cfg.cls = this.cls;
4574         }
4575         if(this.align){
4576             cfg.align = this.align;
4577         }
4578         if(this.bgcolor){
4579             cfg.bgcolor = this.bgcolor;
4580         }
4581         if(this.charoff){
4582             cfg.charoff = this.charoff;
4583         }
4584         if(this.valign){
4585             cfg.valign = this.valign;
4586         }
4587         
4588         return cfg;
4589     }
4590    
4591 });
4592
4593  
4594
4595  /*
4596  * - LGPL
4597  *
4598  * table body
4599  * 
4600  */
4601
4602 /**
4603  * @class Roo.bootstrap.TableBody
4604  * @extends Roo.bootstrap.Component
4605  * Bootstrap TableBody class
4606  * @cfg {String} cls element class
4607  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4608  * @cfg {String} align Aligns the content inside the element
4609  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4610  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4611  * 
4612  * @constructor
4613  * Create a new TableBody
4614  * @param {Object} config The config object
4615  */
4616
4617 Roo.bootstrap.TableBody = function(config){
4618     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4619 };
4620
4621 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4622     
4623     cls: false,
4624     tag: false,
4625     align: false,
4626     charoff: false,
4627     valign: false,
4628     
4629     getAutoCreate : function(){
4630         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4631         
4632         cfg = {
4633             tag: 'tbody'
4634         }
4635             
4636         if (this.cls) {
4637             cfg.cls=this.cls
4638         }
4639         if(this.tag){
4640             cfg.tag = this.tag;
4641         }
4642         
4643         if(this.align){
4644             cfg.align = this.align;
4645         }
4646         if(this.charoff){
4647             cfg.charoff = this.charoff;
4648         }
4649         if(this.valign){
4650             cfg.valign = this.valign;
4651         }
4652         
4653         return cfg;
4654     }
4655     
4656     
4657 //    initEvents : function()
4658 //    {
4659 //        
4660 //        if(!this.store){
4661 //            return;
4662 //        }
4663 //        
4664 //        this.store = Roo.factory(this.store, Roo.data);
4665 //        this.store.on('load', this.onLoad, this);
4666 //        
4667 //        this.store.load();
4668 //        
4669 //    },
4670 //    
4671 //    onLoad: function () 
4672 //    {   
4673 //        this.fireEvent('load', this);
4674 //    }
4675 //    
4676 //   
4677 });
4678
4679  
4680
4681  /*
4682  * Based on:
4683  * Ext JS Library 1.1.1
4684  * Copyright(c) 2006-2007, Ext JS, LLC.
4685  *
4686  * Originally Released Under LGPL - original licence link has changed is not relivant.
4687  *
4688  * Fork - LGPL
4689  * <script type="text/javascript">
4690  */
4691
4692 // as we use this in bootstrap.
4693 Roo.namespace('Roo.form');
4694  /**
4695  * @class Roo.form.Action
4696  * Internal Class used to handle form actions
4697  * @constructor
4698  * @param {Roo.form.BasicForm} el The form element or its id
4699  * @param {Object} config Configuration options
4700  */
4701
4702  
4703  
4704 // define the action interface
4705 Roo.form.Action = function(form, options){
4706     this.form = form;
4707     this.options = options || {};
4708 };
4709 /**
4710  * Client Validation Failed
4711  * @const 
4712  */
4713 Roo.form.Action.CLIENT_INVALID = 'client';
4714 /**
4715  * Server Validation Failed
4716  * @const 
4717  */
4718 Roo.form.Action.SERVER_INVALID = 'server';
4719  /**
4720  * Connect to Server Failed
4721  * @const 
4722  */
4723 Roo.form.Action.CONNECT_FAILURE = 'connect';
4724 /**
4725  * Reading Data from Server Failed
4726  * @const 
4727  */
4728 Roo.form.Action.LOAD_FAILURE = 'load';
4729
4730 Roo.form.Action.prototype = {
4731     type : 'default',
4732     failureType : undefined,
4733     response : undefined,
4734     result : undefined,
4735
4736     // interface method
4737     run : function(options){
4738
4739     },
4740
4741     // interface method
4742     success : function(response){
4743
4744     },
4745
4746     // interface method
4747     handleResponse : function(response){
4748
4749     },
4750
4751     // default connection failure
4752     failure : function(response){
4753         
4754         this.response = response;
4755         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4756         this.form.afterAction(this, false);
4757     },
4758
4759     processResponse : function(response){
4760         this.response = response;
4761         if(!response.responseText){
4762             return true;
4763         }
4764         this.result = this.handleResponse(response);
4765         return this.result;
4766     },
4767
4768     // utility functions used internally
4769     getUrl : function(appendParams){
4770         var url = this.options.url || this.form.url || this.form.el.dom.action;
4771         if(appendParams){
4772             var p = this.getParams();
4773             if(p){
4774                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4775             }
4776         }
4777         return url;
4778     },
4779
4780     getMethod : function(){
4781         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4782     },
4783
4784     getParams : function(){
4785         var bp = this.form.baseParams;
4786         var p = this.options.params;
4787         if(p){
4788             if(typeof p == "object"){
4789                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4790             }else if(typeof p == 'string' && bp){
4791                 p += '&' + Roo.urlEncode(bp);
4792             }
4793         }else if(bp){
4794             p = Roo.urlEncode(bp);
4795         }
4796         return p;
4797     },
4798
4799     createCallback : function(){
4800         return {
4801             success: this.success,
4802             failure: this.failure,
4803             scope: this,
4804             timeout: (this.form.timeout*1000),
4805             upload: this.form.fileUpload ? this.success : undefined
4806         };
4807     }
4808 };
4809
4810 Roo.form.Action.Submit = function(form, options){
4811     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4812 };
4813
4814 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4815     type : 'submit',
4816
4817     haveProgress : false,
4818     uploadComplete : false,
4819     
4820     // uploadProgress indicator.
4821     uploadProgress : function()
4822     {
4823         if (!this.form.progressUrl) {
4824             return;
4825         }
4826         
4827         if (!this.haveProgress) {
4828             Roo.MessageBox.progress("Uploading", "Uploading");
4829         }
4830         if (this.uploadComplete) {
4831            Roo.MessageBox.hide();
4832            return;
4833         }
4834         
4835         this.haveProgress = true;
4836    
4837         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4838         
4839         var c = new Roo.data.Connection();
4840         c.request({
4841             url : this.form.progressUrl,
4842             params: {
4843                 id : uid
4844             },
4845             method: 'GET',
4846             success : function(req){
4847                //console.log(data);
4848                 var rdata = false;
4849                 var edata;
4850                 try  {
4851                    rdata = Roo.decode(req.responseText)
4852                 } catch (e) {
4853                     Roo.log("Invalid data from server..");
4854                     Roo.log(edata);
4855                     return;
4856                 }
4857                 if (!rdata || !rdata.success) {
4858                     Roo.log(rdata);
4859                     Roo.MessageBox.alert(Roo.encode(rdata));
4860                     return;
4861                 }
4862                 var data = rdata.data;
4863                 
4864                 if (this.uploadComplete) {
4865                    Roo.MessageBox.hide();
4866                    return;
4867                 }
4868                    
4869                 if (data){
4870                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4871                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4872                     );
4873                 }
4874                 this.uploadProgress.defer(2000,this);
4875             },
4876        
4877             failure: function(data) {
4878                 Roo.log('progress url failed ');
4879                 Roo.log(data);
4880             },
4881             scope : this
4882         });
4883            
4884     },
4885     
4886     
4887     run : function()
4888     {
4889         // run get Values on the form, so it syncs any secondary forms.
4890         this.form.getValues();
4891         
4892         var o = this.options;
4893         var method = this.getMethod();
4894         var isPost = method == 'POST';
4895         if(o.clientValidation === false || this.form.isValid()){
4896             
4897             if (this.form.progressUrl) {
4898                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4899                     (new Date() * 1) + '' + Math.random());
4900                     
4901             } 
4902             
4903             
4904             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4905                 form:this.form.el.dom,
4906                 url:this.getUrl(!isPost),
4907                 method: method,
4908                 params:isPost ? this.getParams() : null,
4909                 isUpload: this.form.fileUpload
4910             }));
4911             
4912             this.uploadProgress();
4913
4914         }else if (o.clientValidation !== false){ // client validation failed
4915             this.failureType = Roo.form.Action.CLIENT_INVALID;
4916             this.form.afterAction(this, false);
4917         }
4918     },
4919
4920     success : function(response)
4921     {
4922         this.uploadComplete= true;
4923         if (this.haveProgress) {
4924             Roo.MessageBox.hide();
4925         }
4926         
4927         
4928         var result = this.processResponse(response);
4929         if(result === true || result.success){
4930             this.form.afterAction(this, true);
4931             return;
4932         }
4933         if(result.errors){
4934             this.form.markInvalid(result.errors);
4935             this.failureType = Roo.form.Action.SERVER_INVALID;
4936         }
4937         this.form.afterAction(this, false);
4938     },
4939     failure : function(response)
4940     {
4941         this.uploadComplete= true;
4942         if (this.haveProgress) {
4943             Roo.MessageBox.hide();
4944         }
4945         
4946         this.response = response;
4947         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4948         this.form.afterAction(this, false);
4949     },
4950     
4951     handleResponse : function(response){
4952         if(this.form.errorReader){
4953             var rs = this.form.errorReader.read(response);
4954             var errors = [];
4955             if(rs.records){
4956                 for(var i = 0, len = rs.records.length; i < len; i++) {
4957                     var r = rs.records[i];
4958                     errors[i] = r.data;
4959                 }
4960             }
4961             if(errors.length < 1){
4962                 errors = null;
4963             }
4964             return {
4965                 success : rs.success,
4966                 errors : errors
4967             };
4968         }
4969         var ret = false;
4970         try {
4971             ret = Roo.decode(response.responseText);
4972         } catch (e) {
4973             ret = {
4974                 success: false,
4975                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4976                 errors : []
4977             };
4978         }
4979         return ret;
4980         
4981     }
4982 });
4983
4984
4985 Roo.form.Action.Load = function(form, options){
4986     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4987     this.reader = this.form.reader;
4988 };
4989
4990 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4991     type : 'load',
4992
4993     run : function(){
4994         
4995         Roo.Ajax.request(Roo.apply(
4996                 this.createCallback(), {
4997                     method:this.getMethod(),
4998                     url:this.getUrl(false),
4999                     params:this.getParams()
5000         }));
5001     },
5002
5003     success : function(response){
5004         
5005         var result = this.processResponse(response);
5006         if(result === true || !result.success || !result.data){
5007             this.failureType = Roo.form.Action.LOAD_FAILURE;
5008             this.form.afterAction(this, false);
5009             return;
5010         }
5011         this.form.clearInvalid();
5012         this.form.setValues(result.data);
5013         this.form.afterAction(this, true);
5014     },
5015
5016     handleResponse : function(response){
5017         if(this.form.reader){
5018             var rs = this.form.reader.read(response);
5019             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5020             return {
5021                 success : rs.success,
5022                 data : data
5023             };
5024         }
5025         return Roo.decode(response.responseText);
5026     }
5027 });
5028
5029 Roo.form.Action.ACTION_TYPES = {
5030     'load' : Roo.form.Action.Load,
5031     'submit' : Roo.form.Action.Submit
5032 };/*
5033  * - LGPL
5034  *
5035  * form
5036  * 
5037  */
5038
5039 /**
5040  * @class Roo.bootstrap.Form
5041  * @extends Roo.bootstrap.Component
5042  * Bootstrap Form class
5043  * @cfg {String} method  GET | POST (default POST)
5044  * @cfg {String} labelAlign top | left (default top)
5045   * @cfg {String} align left  | right - for navbars
5046
5047  * 
5048  * @constructor
5049  * Create a new Form
5050  * @param {Object} config The config object
5051  */
5052
5053
5054 Roo.bootstrap.Form = function(config){
5055     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5056     this.addEvents({
5057         /**
5058          * @event clientvalidation
5059          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5060          * @param {Form} this
5061          * @param {Boolean} valid true if the form has passed client-side validation
5062          */
5063         clientvalidation: true,
5064         /**
5065          * @event beforeaction
5066          * Fires before any action is performed. Return false to cancel the action.
5067          * @param {Form} this
5068          * @param {Action} action The action to be performed
5069          */
5070         beforeaction: true,
5071         /**
5072          * @event actionfailed
5073          * Fires when an action fails.
5074          * @param {Form} this
5075          * @param {Action} action The action that failed
5076          */
5077         actionfailed : true,
5078         /**
5079          * @event actioncomplete
5080          * Fires when an action is completed.
5081          * @param {Form} this
5082          * @param {Action} action The action that completed
5083          */
5084         actioncomplete : true
5085     });
5086     
5087 };
5088
5089 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5090       
5091      /**
5092      * @cfg {String} method
5093      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5094      */
5095     method : 'POST',
5096     /**
5097      * @cfg {String} url
5098      * The URL to use for form actions if one isn't supplied in the action options.
5099      */
5100     /**
5101      * @cfg {Boolean} fileUpload
5102      * Set to true if this form is a file upload.
5103      */
5104      
5105     /**
5106      * @cfg {Object} baseParams
5107      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5108      */
5109       
5110     /**
5111      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5112      */
5113     timeout: 30,
5114     /**
5115      * @cfg {Sting} align (left|right) for navbar forms
5116      */
5117     align : 'left',
5118
5119     // private
5120     activeAction : null,
5121  
5122     /**
5123      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5124      * element by passing it or its id or mask the form itself by passing in true.
5125      * @type Mixed
5126      */
5127     waitMsgTarget : false,
5128     
5129      
5130     
5131     /**
5132      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5133      * element by passing it or its id or mask the form itself by passing in true.
5134      * @type Mixed
5135      */
5136     
5137     getAutoCreate : function(){
5138         
5139         var cfg = {
5140             tag: 'form',
5141             method : this.method || 'POST',
5142             id : this.id || Roo.id(),
5143             cls : ''
5144         }
5145         if (this.parent().xtype.match(/^Nav/)) {
5146             cfg.cls = 'navbar-form navbar-' + this.align;
5147             
5148         }
5149         
5150         if (this.labelAlign == 'left' ) {
5151             cfg.cls += ' form-horizontal';
5152         }
5153         
5154         
5155         return cfg;
5156     },
5157     initEvents : function()
5158     {
5159         this.el.on('submit', this.onSubmit, this);
5160         
5161         
5162     },
5163     // private
5164     onSubmit : function(e){
5165         e.stopEvent();
5166     },
5167     
5168      /**
5169      * Returns true if client-side validation on the form is successful.
5170      * @return Boolean
5171      */
5172     isValid : function(){
5173         var items = this.getItems();
5174         var valid = true;
5175         items.each(function(f){
5176            if(!f.validate()){
5177                valid = false;
5178                
5179            }
5180         });
5181         return valid;
5182     },
5183     /**
5184      * Returns true if any fields in this form have changed since their original load.
5185      * @return Boolean
5186      */
5187     isDirty : function(){
5188         var dirty = false;
5189         var items = this.getItems();
5190         items.each(function(f){
5191            if(f.isDirty()){
5192                dirty = true;
5193                return false;
5194            }
5195            return true;
5196         });
5197         return dirty;
5198     },
5199      /**
5200      * Performs a predefined action (submit or load) or custom actions you define on this form.
5201      * @param {String} actionName The name of the action type
5202      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5203      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5204      * accept other config options):
5205      * <pre>
5206 Property          Type             Description
5207 ----------------  ---------------  ----------------------------------------------------------------------------------
5208 url               String           The url for the action (defaults to the form's url)
5209 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5210 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5211 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5212                                    validate the form on the client (defaults to false)
5213      * </pre>
5214      * @return {BasicForm} this
5215      */
5216     doAction : function(action, options){
5217         if(typeof action == 'string'){
5218             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5219         }
5220         if(this.fireEvent('beforeaction', this, action) !== false){
5221             this.beforeAction(action);
5222             action.run.defer(100, action);
5223         }
5224         return this;
5225     },
5226     
5227     // private
5228     beforeAction : function(action){
5229         var o = action.options;
5230         
5231         // not really supported yet.. ??
5232         
5233         //if(this.waitMsgTarget === true){
5234             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5235         //}else if(this.waitMsgTarget){
5236         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5237         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5238         //}else {
5239         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5240        // }
5241          
5242     },
5243
5244     // private
5245     afterAction : function(action, success){
5246         this.activeAction = null;
5247         var o = action.options;
5248         
5249         //if(this.waitMsgTarget === true){
5250             this.el.unmask();
5251         //}else if(this.waitMsgTarget){
5252         //    this.waitMsgTarget.unmask();
5253         //}else{
5254         //    Roo.MessageBox.updateProgress(1);
5255         //    Roo.MessageBox.hide();
5256        // }
5257         // 
5258         if(success){
5259             if(o.reset){
5260                 this.reset();
5261             }
5262             Roo.callback(o.success, o.scope, [this, action]);
5263             this.fireEvent('actioncomplete', this, action);
5264             
5265         }else{
5266             
5267             // failure condition..
5268             // we have a scenario where updates need confirming.
5269             // eg. if a locking scenario exists..
5270             // we look for { errors : { needs_confirm : true }} in the response.
5271             if (
5272                 (typeof(action.result) != 'undefined')  &&
5273                 (typeof(action.result.errors) != 'undefined')  &&
5274                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5275            ){
5276                 var _t = this;
5277                 Roo.log("not supported yet");
5278                  /*
5279                 
5280                 Roo.MessageBox.confirm(
5281                     "Change requires confirmation",
5282                     action.result.errorMsg,
5283                     function(r) {
5284                         if (r != 'yes') {
5285                             return;
5286                         }
5287                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5288                     }
5289                     
5290                 );
5291                 */
5292                 
5293                 
5294                 return;
5295             }
5296             
5297             Roo.callback(o.failure, o.scope, [this, action]);
5298             // show an error message if no failed handler is set..
5299             if (!this.hasListener('actionfailed')) {
5300                 Roo.log("need to add dialog support");
5301                 /*
5302                 Roo.MessageBox.alert("Error",
5303                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5304                         action.result.errorMsg :
5305                         "Saving Failed, please check your entries or try again"
5306                 );
5307                 */
5308             }
5309             
5310             this.fireEvent('actionfailed', this, action);
5311         }
5312         
5313     },
5314     /**
5315      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5316      * @param {String} id The value to search for
5317      * @return Field
5318      */
5319     findField : function(id){
5320         var items = this.getItems();
5321         var field = items.get(id);
5322         if(!field){
5323              items.each(function(f){
5324                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5325                     field = f;
5326                     return false;
5327                 }
5328                 return true;
5329             });
5330         }
5331         return field || null;
5332     },
5333      /**
5334      * Mark fields in this form invalid in bulk.
5335      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5336      * @return {BasicForm} this
5337      */
5338     markInvalid : function(errors){
5339         if(errors instanceof Array){
5340             for(var i = 0, len = errors.length; i < len; i++){
5341                 var fieldError = errors[i];
5342                 var f = this.findField(fieldError.id);
5343                 if(f){
5344                     f.markInvalid(fieldError.msg);
5345                 }
5346             }
5347         }else{
5348             var field, id;
5349             for(id in errors){
5350                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5351                     field.markInvalid(errors[id]);
5352                 }
5353             }
5354         }
5355         //Roo.each(this.childForms || [], function (f) {
5356         //    f.markInvalid(errors);
5357         //});
5358         
5359         return this;
5360     },
5361
5362     /**
5363      * Set values for fields in this form in bulk.
5364      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5365      * @return {BasicForm} this
5366      */
5367     setValues : function(values){
5368         if(values instanceof Array){ // array of objects
5369             for(var i = 0, len = values.length; i < len; i++){
5370                 var v = values[i];
5371                 var f = this.findField(v.id);
5372                 if(f){
5373                     f.setValue(v.value);
5374                     if(this.trackResetOnLoad){
5375                         f.originalValue = f.getValue();
5376                     }
5377                 }
5378             }
5379         }else{ // object hash
5380             var field, id;
5381             for(id in values){
5382                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5383                     
5384                     if (field.setFromData && 
5385                         field.valueField && 
5386                         field.displayField &&
5387                         // combos' with local stores can 
5388                         // be queried via setValue()
5389                         // to set their value..
5390                         (field.store && !field.store.isLocal)
5391                         ) {
5392                         // it's a combo
5393                         var sd = { };
5394                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5395                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5396                         field.setFromData(sd);
5397                         
5398                     } else {
5399                         field.setValue(values[id]);
5400                     }
5401                     
5402                     
5403                     if(this.trackResetOnLoad){
5404                         field.originalValue = field.getValue();
5405                     }
5406                 }
5407             }
5408         }
5409          
5410         //Roo.each(this.childForms || [], function (f) {
5411         //    f.setValues(values);
5412         //});
5413                 
5414         return this;
5415     },
5416
5417     /**
5418      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5419      * they are returned as an array.
5420      * @param {Boolean} asString
5421      * @return {Object}
5422      */
5423     getValues : function(asString){
5424         //if (this.childForms) {
5425             // copy values from the child forms
5426         //    Roo.each(this.childForms, function (f) {
5427         //        this.setValues(f.getValues());
5428         //    }, this);
5429         //}
5430         
5431         
5432         
5433         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5434         if(asString === true){
5435             return fs;
5436         }
5437         return Roo.urlDecode(fs);
5438     },
5439     
5440     /**
5441      * Returns the fields in this form as an object with key/value pairs. 
5442      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5443      * @return {Object}
5444      */
5445     getFieldValues : function(with_hidden)
5446     {
5447         var items = this.getItems();
5448         var ret = {};
5449         items.each(function(f){
5450             if (!f.getName()) {
5451                 return;
5452             }
5453             var v = f.getValue();
5454             if (f.inputType =='radio') {
5455                 if (typeof(ret[f.getName()]) == 'undefined') {
5456                     ret[f.getName()] = ''; // empty..
5457                 }
5458                 
5459                 if (!f.el.dom.checked) {
5460                     return;
5461                     
5462                 }
5463                 v = f.el.dom.value;
5464                 
5465             }
5466             
5467             // not sure if this supported any more..
5468             if ((typeof(v) == 'object') && f.getRawValue) {
5469                 v = f.getRawValue() ; // dates..
5470             }
5471             // combo boxes where name != hiddenName...
5472             if (f.name != f.getName()) {
5473                 ret[f.name] = f.getRawValue();
5474             }
5475             ret[f.getName()] = v;
5476         });
5477         
5478         return ret;
5479     },
5480
5481     /**
5482      * Clears all invalid messages in this form.
5483      * @return {BasicForm} this
5484      */
5485     clearInvalid : function(){
5486         var items = this.getItems();
5487         
5488         items.each(function(f){
5489            f.clearInvalid();
5490         });
5491         
5492         
5493         
5494         return this;
5495     },
5496
5497     /**
5498      * Resets this form.
5499      * @return {BasicForm} this
5500      */
5501     reset : function(){
5502         var items = this.getItems();
5503         items.each(function(f){
5504             f.reset();
5505         });
5506         
5507         Roo.each(this.childForms || [], function (f) {
5508             f.reset();
5509         });
5510        
5511         
5512         return this;
5513     },
5514     getItems : function()
5515     {
5516         var r=new Roo.util.MixedCollection(false, function(o){
5517             return o.id || (o.id = Roo.id());
5518         });
5519         var iter = function(el) {
5520             if (el.inputEl) {
5521                 r.add(el);
5522             }
5523             if (!el.items) {
5524                 return;
5525             }
5526             Roo.each(el.items,function(e) {
5527                 iter(e);
5528             });
5529             
5530             
5531         };
5532         iter(this);
5533         return r;
5534         
5535         
5536         
5537         
5538     }
5539     
5540 });
5541
5542  
5543 /*
5544  * Based on:
5545  * Ext JS Library 1.1.1
5546  * Copyright(c) 2006-2007, Ext JS, LLC.
5547  *
5548  * Originally Released Under LGPL - original licence link has changed is not relivant.
5549  *
5550  * Fork - LGPL
5551  * <script type="text/javascript">
5552  */
5553 /**
5554  * @class Roo.form.VTypes
5555  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5556  * @singleton
5557  */
5558 Roo.form.VTypes = function(){
5559     // closure these in so they are only created once.
5560     var alpha = /^[a-zA-Z_]+$/;
5561     var alphanum = /^[a-zA-Z0-9_]+$/;
5562     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5563     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5564
5565     // All these messages and functions are configurable
5566     return {
5567         /**
5568          * The function used to validate email addresses
5569          * @param {String} value The email address
5570          */
5571         'email' : function(v){
5572             return email.test(v);
5573         },
5574         /**
5575          * The error text to display when the email validation function returns false
5576          * @type String
5577          */
5578         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5579         /**
5580          * The keystroke filter mask to be applied on email input
5581          * @type RegExp
5582          */
5583         'emailMask' : /[a-z0-9_\.\-@]/i,
5584
5585         /**
5586          * The function used to validate URLs
5587          * @param {String} value The URL
5588          */
5589         'url' : function(v){
5590             return url.test(v);
5591         },
5592         /**
5593          * The error text to display when the url validation function returns false
5594          * @type String
5595          */
5596         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5597         
5598         /**
5599          * The function used to validate alpha values
5600          * @param {String} value The value
5601          */
5602         'alpha' : function(v){
5603             return alpha.test(v);
5604         },
5605         /**
5606          * The error text to display when the alpha validation function returns false
5607          * @type String
5608          */
5609         'alphaText' : 'This field should only contain letters and _',
5610         /**
5611          * The keystroke filter mask to be applied on alpha input
5612          * @type RegExp
5613          */
5614         'alphaMask' : /[a-z_]/i,
5615
5616         /**
5617          * The function used to validate alphanumeric values
5618          * @param {String} value The value
5619          */
5620         'alphanum' : function(v){
5621             return alphanum.test(v);
5622         },
5623         /**
5624          * The error text to display when the alphanumeric validation function returns false
5625          * @type String
5626          */
5627         'alphanumText' : 'This field should only contain letters, numbers and _',
5628         /**
5629          * The keystroke filter mask to be applied on alphanumeric input
5630          * @type RegExp
5631          */
5632         'alphanumMask' : /[a-z0-9_]/i
5633     };
5634 }();/*
5635  * - LGPL
5636  *
5637  * Input
5638  * 
5639  */
5640
5641 /**
5642  * @class Roo.bootstrap.Input
5643  * @extends Roo.bootstrap.Component
5644  * Bootstrap Input class
5645  * @cfg {Boolean} disabled is it disabled
5646  * @cfg {String} fieldLabel - the label associated
5647  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5648  * @cfg {String} name name of the input
5649  * @cfg {string} fieldLabel - the label associated
5650  * @cfg {string}  inputType - input / file submit ...
5651  * @cfg {string} placeholder - placeholder to put in text.
5652  * @cfg {string}  before - input group add on before
5653  * @cfg {string} after - input group add on after
5654  * @cfg {string} size - (lg|sm) or leave empty..
5655  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5656  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5657  * @cfg {Number} md colspan out of 12 for computer-sized screens
5658  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5659  * @cfg {string} value default value of the input
5660  * @cfg {Number} labelWidth set the width of label (0-12)
5661  * @cfg {String} labelAlign (top|left)
5662  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5663  * 
5664  * 
5665  * @constructor
5666  * Create a new Input
5667  * @param {Object} config The config object
5668  */
5669
5670 Roo.bootstrap.Input = function(config){
5671     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5672    
5673         this.addEvents({
5674             /**
5675              * @event focus
5676              * Fires when this field receives input focus.
5677              * @param {Roo.form.Field} this
5678              */
5679             focus : true,
5680             /**
5681              * @event blur
5682              * Fires when this field loses input focus.
5683              * @param {Roo.form.Field} this
5684              */
5685             blur : true,
5686             /**
5687              * @event specialkey
5688              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5689              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5690              * @param {Roo.form.Field} this
5691              * @param {Roo.EventObject} e The event object
5692              */
5693             specialkey : true,
5694             /**
5695              * @event change
5696              * Fires just before the field blurs if the field value has changed.
5697              * @param {Roo.form.Field} this
5698              * @param {Mixed} newValue The new value
5699              * @param {Mixed} oldValue The original value
5700              */
5701             change : true,
5702             /**
5703              * @event invalid
5704              * Fires after the field has been marked as invalid.
5705              * @param {Roo.form.Field} this
5706              * @param {String} msg The validation message
5707              */
5708             invalid : true,
5709             /**
5710              * @event valid
5711              * Fires after the field has been validated with no errors.
5712              * @param {Roo.form.Field} this
5713              */
5714             valid : true,
5715              /**
5716              * @event keyup
5717              * Fires after the key up
5718              * @param {Roo.form.Field} this
5719              * @param {Roo.EventObject}  e The event Object
5720              */
5721             keyup : true
5722         });
5723 };
5724
5725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5726      /**
5727      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5728       automatic validation (defaults to "keyup").
5729      */
5730     validationEvent : "keyup",
5731      /**
5732      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5733      */
5734     validateOnBlur : true,
5735     /**
5736      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5737      */
5738     validationDelay : 250,
5739      /**
5740      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5741      */
5742     focusClass : "x-form-focus",  // not needed???
5743     
5744        
5745     /**
5746      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5747      */
5748     invalidClass : "has-error",
5749     
5750     /**
5751      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5752      */
5753     selectOnFocus : false,
5754     
5755      /**
5756      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5757      */
5758     maskRe : null,
5759        /**
5760      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5761      */
5762     vtype : null,
5763     
5764       /**
5765      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5766      */
5767     disableKeyFilter : false,
5768     
5769        /**
5770      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5771      */
5772     disabled : false,
5773      /**
5774      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5775      */
5776     allowBlank : true,
5777     /**
5778      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5779      */
5780     blankText : "This field is required",
5781     
5782      /**
5783      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5784      */
5785     minLength : 0,
5786     /**
5787      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5788      */
5789     maxLength : Number.MAX_VALUE,
5790     /**
5791      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5792      */
5793     minLengthText : "The minimum length for this field is {0}",
5794     /**
5795      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5796      */
5797     maxLengthText : "The maximum length for this field is {0}",
5798   
5799     
5800     /**
5801      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5802      * If available, this function will be called only after the basic validators all return true, and will be passed the
5803      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5804      */
5805     validator : null,
5806     /**
5807      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5808      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5809      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5810      */
5811     regex : null,
5812     /**
5813      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5814      */
5815     regexText : "",
5816     
5817     
5818     
5819     fieldLabel : '',
5820     inputType : 'text',
5821     
5822     name : false,
5823     placeholder: false,
5824     before : false,
5825     after : false,
5826     size : false,
5827     // private
5828     hasFocus : false,
5829     preventMark: false,
5830     isFormField : true,
5831     value : '',
5832     labelWidth : 2,
5833     labelAlign : false,
5834     readOnly : false,
5835     
5836     parentLabelAlign : function()
5837     {
5838         var parent = this;
5839         while (parent.parent()) {
5840             parent = parent.parent();
5841             if (typeof(parent.labelAlign) !='undefined') {
5842                 return parent.labelAlign;
5843             }
5844         }
5845         return 'left';
5846         
5847     },
5848     
5849     getAutoCreate : function(){
5850         
5851         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5852         
5853         var id = Roo.id();
5854         
5855         var cfg = {};
5856         
5857         if(this.inputType != 'hidden'){
5858             cfg.cls = 'form-group' //input-group
5859         }
5860         
5861         var input =  {
5862             tag: 'input',
5863             id : id,
5864             type : this.inputType,
5865             value : this.value,
5866             cls : 'form-control',
5867             placeholder : this.placeholder || ''
5868             
5869         };
5870         
5871         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5872             input.maxLength = this.maxLength;
5873         }
5874         
5875         if (this.disabled) {
5876             input.disabled=true;
5877         }
5878         
5879         if (this.readOnly) {
5880             input.readonly=true;
5881         }
5882         
5883         if (this.name) {
5884             input.name = this.name;
5885         }
5886         if (this.size) {
5887             input.cls += ' input-' + this.size;
5888         }
5889         var settings=this;
5890         ['xs','sm','md','lg'].map(function(size){
5891             if (settings[size]) {
5892                 cfg.cls += ' col-' + size + '-' + settings[size];
5893             }
5894         });
5895         
5896         var inputblock = input;
5897         
5898         if (this.before || this.after) {
5899             
5900             inputblock = {
5901                 cls : 'input-group',
5902                 cn :  [] 
5903             };
5904             if (this.before && typeof(this.before) == 'string') {
5905                 
5906                 inputblock.cn.push({
5907                     tag :'span',
5908                     cls : 'roo-input-before input-group-addon',
5909                     html : this.before
5910                 });
5911             }
5912             if (this.before && typeof(this.before) == 'object') {
5913                 this.before = Roo.factory(this.before);
5914                 Roo.log(this.before);
5915                 inputblock.cn.push({
5916                     tag :'span',
5917                     cls : 'roo-input-before input-group-' +
5918                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5919                 });
5920             }
5921             
5922             inputblock.cn.push(input);
5923             
5924             if (this.after && typeof(this.after) == 'string') {
5925                 inputblock.cn.push({
5926                     tag :'span',
5927                     cls : 'roo-input-after input-group-addon',
5928                     html : this.after
5929                 });
5930             }
5931             if (this.after && typeof(this.after) == 'object') {
5932                 this.after = Roo.factory(this.after);
5933                 Roo.log(this.after);
5934                 inputblock.cn.push({
5935                     tag :'span',
5936                     cls : 'roo-input-after input-group-' +
5937                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5938                 });
5939             }
5940         };
5941         
5942         if (align ==='left' && this.fieldLabel.length) {
5943                 Roo.log("left and has label");
5944                 cfg.cn = [
5945                     
5946                     {
5947                         tag: 'label',
5948                         'for' :  id,
5949                         cls : 'control-label col-sm-' + this.labelWidth,
5950                         html : this.fieldLabel
5951                         
5952                     },
5953                     {
5954                         cls : "col-sm-" + (12 - this.labelWidth), 
5955                         cn: [
5956                             inputblock
5957                         ]
5958                     }
5959                     
5960                 ];
5961         } else if ( this.fieldLabel.length) {
5962                 Roo.log(" label");
5963                  cfg.cn = [
5964                    
5965                     {
5966                         tag: 'label',
5967                         //cls : 'input-group-addon',
5968                         html : this.fieldLabel
5969                         
5970                     },
5971                     
5972                     inputblock
5973                     
5974                 ];
5975
5976         } else {
5977             
5978                 Roo.log(" no label && no align");
5979                 cfg.cn = [
5980                     
5981                         inputblock
5982                     
5983                 ];
5984                 
5985                 
5986         };
5987         Roo.log('input-parentType: ' + this.parentType);
5988         
5989         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5990            cfg.cls += ' navbar-form';
5991            Roo.log(cfg);
5992         }
5993         
5994         return cfg;
5995         
5996     },
5997     /**
5998      * return the real input element.
5999      */
6000     inputEl: function ()
6001     {
6002         return this.el.select('input.form-control',true).first();
6003     },
6004     setDisabled : function(v)
6005     {
6006         var i  = this.inputEl().dom;
6007         if (!v) {
6008             i.removeAttribute('disabled');
6009             return;
6010             
6011         }
6012         i.setAttribute('disabled','true');
6013     },
6014     initEvents : function()
6015     {
6016         
6017         this.inputEl().on("keydown" , this.fireKey,  this);
6018         this.inputEl().on("focus", this.onFocus,  this);
6019         this.inputEl().on("blur", this.onBlur,  this);
6020         
6021         this.inputEl().relayEvent('keyup', this);
6022
6023         // reference to original value for reset
6024         this.originalValue = this.getValue();
6025         //Roo.form.TextField.superclass.initEvents.call(this);
6026         if(this.validationEvent == 'keyup'){
6027             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6028             this.inputEl().on('keyup', this.filterValidation, this);
6029         }
6030         else if(this.validationEvent !== false){
6031             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6032         }
6033         
6034         if(this.selectOnFocus){
6035             this.on("focus", this.preFocus, this);
6036             
6037         }
6038         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6039             this.inputEl().on("keypress", this.filterKeys, this);
6040         }
6041        /* if(this.grow){
6042             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6043             this.el.on("click", this.autoSize,  this);
6044         }
6045         */
6046         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6047             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6048         }
6049         
6050         if (typeof(this.before) == 'object') {
6051             this.before.render(this.el.select('.roo-input-before',true).first());
6052         }
6053         if (typeof(this.after) == 'object') {
6054             this.after.render(this.el.select('.roo-input-after',true).first());
6055         }
6056         
6057         
6058     },
6059     filterValidation : function(e){
6060         if(!e.isNavKeyPress()){
6061             this.validationTask.delay(this.validationDelay);
6062         }
6063     },
6064      /**
6065      * Validates the field value
6066      * @return {Boolean} True if the value is valid, else false
6067      */
6068     validate : function(){
6069         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6070         if(this.disabled || this.validateValue(this.getRawValue())){
6071             this.clearInvalid();
6072             return true;
6073         }
6074         return false;
6075     },
6076     
6077     
6078     /**
6079      * Validates a value according to the field's validation rules and marks the field as invalid
6080      * if the validation fails
6081      * @param {Mixed} value The value to validate
6082      * @return {Boolean} True if the value is valid, else false
6083      */
6084     validateValue : function(value){
6085         if(value.length < 1)  { // if it's blank
6086              if(this.allowBlank){
6087                 this.clearInvalid();
6088                 return true;
6089              }else{
6090                 this.markInvalid(this.blankText);
6091                 return false;
6092              }
6093         }
6094         if(value.length < this.minLength){
6095             this.markInvalid(String.format(this.minLengthText, this.minLength));
6096             return false;
6097         }
6098         if(value.length > this.maxLength){
6099             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6100             return false;
6101         }
6102         if(this.vtype){
6103             var vt = Roo.form.VTypes;
6104             if(!vt[this.vtype](value, this)){
6105                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6106                 return false;
6107             }
6108         }
6109         if(typeof this.validator == "function"){
6110             var msg = this.validator(value);
6111             if(msg !== true){
6112                 this.markInvalid(msg);
6113                 return false;
6114             }
6115         }
6116         if(this.regex && !this.regex.test(value)){
6117             this.markInvalid(this.regexText);
6118             return false;
6119         }
6120         return true;
6121     },
6122
6123     
6124     
6125      // private
6126     fireKey : function(e){
6127         //Roo.log('field ' + e.getKey());
6128         if(e.isNavKeyPress()){
6129             this.fireEvent("specialkey", this, e);
6130         }
6131     },
6132     focus : function (selectText){
6133         if(this.rendered){
6134             this.inputEl().focus();
6135             if(selectText === true){
6136                 this.inputEl().dom.select();
6137             }
6138         }
6139         return this;
6140     } ,
6141     
6142     onFocus : function(){
6143         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6144            // this.el.addClass(this.focusClass);
6145         }
6146         if(!this.hasFocus){
6147             this.hasFocus = true;
6148             this.startValue = this.getValue();
6149             this.fireEvent("focus", this);
6150         }
6151     },
6152     
6153     beforeBlur : Roo.emptyFn,
6154
6155     
6156     // private
6157     onBlur : function(){
6158         this.beforeBlur();
6159         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6160             //this.el.removeClass(this.focusClass);
6161         }
6162         this.hasFocus = false;
6163         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6164             this.validate();
6165         }
6166         var v = this.getValue();
6167         if(String(v) !== String(this.startValue)){
6168             this.fireEvent('change', this, v, this.startValue);
6169         }
6170         this.fireEvent("blur", this);
6171     },
6172     
6173     /**
6174      * Resets the current field value to the originally loaded value and clears any validation messages
6175      */
6176     reset : function(){
6177         this.setValue(this.originalValue);
6178         this.clearInvalid();
6179     },
6180      /**
6181      * Returns the name of the field
6182      * @return {Mixed} name The name field
6183      */
6184     getName: function(){
6185         return this.name;
6186     },
6187      /**
6188      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6189      * @return {Mixed} value The field value
6190      */
6191     getValue : function(){
6192         return this.inputEl().getValue();
6193     },
6194     /**
6195      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6196      * @return {Mixed} value The field value
6197      */
6198     getRawValue : function(){
6199         var v = this.inputEl().getValue();
6200         
6201         return v;
6202     },
6203     
6204     /**
6205      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6206      * @param {Mixed} value The value to set
6207      */
6208     setRawValue : function(v){
6209         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6210     },
6211     
6212     selectText : function(start, end){
6213         var v = this.getRawValue();
6214         if(v.length > 0){
6215             start = start === undefined ? 0 : start;
6216             end = end === undefined ? v.length : end;
6217             var d = this.inputEl().dom;
6218             if(d.setSelectionRange){
6219                 d.setSelectionRange(start, end);
6220             }else if(d.createTextRange){
6221                 var range = d.createTextRange();
6222                 range.moveStart("character", start);
6223                 range.moveEnd("character", v.length-end);
6224                 range.select();
6225             }
6226         }
6227     },
6228     
6229     /**
6230      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6231      * @param {Mixed} value The value to set
6232      */
6233     setValue : function(v){
6234         this.value = v;
6235         if(this.rendered){
6236             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6237             this.validate();
6238         }
6239     },
6240     
6241     /*
6242     processValue : function(value){
6243         if(this.stripCharsRe){
6244             var newValue = value.replace(this.stripCharsRe, '');
6245             if(newValue !== value){
6246                 this.setRawValue(newValue);
6247                 return newValue;
6248             }
6249         }
6250         return value;
6251     },
6252   */
6253     preFocus : function(){
6254         
6255         if(this.selectOnFocus){
6256             this.inputEl().dom.select();
6257         }
6258     },
6259     filterKeys : function(e){
6260         var k = e.getKey();
6261         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6262             return;
6263         }
6264         var c = e.getCharCode(), cc = String.fromCharCode(c);
6265         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6266             return;
6267         }
6268         if(!this.maskRe.test(cc)){
6269             e.stopEvent();
6270         }
6271     },
6272      /**
6273      * Clear any invalid styles/messages for this field
6274      */
6275     clearInvalid : function(){
6276         
6277         if(!this.el || this.preventMark){ // not rendered
6278             return;
6279         }
6280         this.el.removeClass(this.invalidClass);
6281         /*
6282         switch(this.msgTarget){
6283             case 'qtip':
6284                 this.el.dom.qtip = '';
6285                 break;
6286             case 'title':
6287                 this.el.dom.title = '';
6288                 break;
6289             case 'under':
6290                 if(this.errorEl){
6291                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6292                 }
6293                 break;
6294             case 'side':
6295                 if(this.errorIcon){
6296                     this.errorIcon.dom.qtip = '';
6297                     this.errorIcon.hide();
6298                     this.un('resize', this.alignErrorIcon, this);
6299                 }
6300                 break;
6301             default:
6302                 var t = Roo.getDom(this.msgTarget);
6303                 t.innerHTML = '';
6304                 t.style.display = 'none';
6305                 break;
6306         }
6307         */
6308         this.fireEvent('valid', this);
6309     },
6310      /**
6311      * Mark this field as invalid
6312      * @param {String} msg The validation message
6313      */
6314     markInvalid : function(msg){
6315         if(!this.el  || this.preventMark){ // not rendered
6316             return;
6317         }
6318         this.el.addClass(this.invalidClass);
6319         /*
6320         msg = msg || this.invalidText;
6321         switch(this.msgTarget){
6322             case 'qtip':
6323                 this.el.dom.qtip = msg;
6324                 this.el.dom.qclass = 'x-form-invalid-tip';
6325                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6326                     Roo.QuickTips.enable();
6327                 }
6328                 break;
6329             case 'title':
6330                 this.el.dom.title = msg;
6331                 break;
6332             case 'under':
6333                 if(!this.errorEl){
6334                     var elp = this.el.findParent('.x-form-element', 5, true);
6335                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6336                     this.errorEl.setWidth(elp.getWidth(true)-20);
6337                 }
6338                 this.errorEl.update(msg);
6339                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6340                 break;
6341             case 'side':
6342                 if(!this.errorIcon){
6343                     var elp = this.el.findParent('.x-form-element', 5, true);
6344                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6345                 }
6346                 this.alignErrorIcon();
6347                 this.errorIcon.dom.qtip = msg;
6348                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6349                 this.errorIcon.show();
6350                 this.on('resize', this.alignErrorIcon, this);
6351                 break;
6352             default:
6353                 var t = Roo.getDom(this.msgTarget);
6354                 t.innerHTML = msg;
6355                 t.style.display = this.msgDisplay;
6356                 break;
6357         }
6358         */
6359         this.fireEvent('invalid', this, msg);
6360     },
6361     // private
6362     SafariOnKeyDown : function(event)
6363     {
6364         // this is a workaround for a password hang bug on chrome/ webkit.
6365         
6366         var isSelectAll = false;
6367         
6368         if(this.inputEl().dom.selectionEnd > 0){
6369             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6370         }
6371         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6372             event.preventDefault();
6373             this.setValue('');
6374             return;
6375         }
6376         
6377         if(isSelectAll){ // backspace and delete key
6378             
6379             event.preventDefault();
6380             // this is very hacky as keydown always get's upper case.
6381             //
6382             var cc = String.fromCharCode(event.getCharCode());
6383             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6384             
6385         }
6386     },
6387     adjustWidth : function(tag, w){
6388         tag = tag.toLowerCase();
6389         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6390             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6391                 if(tag == 'input'){
6392                     return w + 2;
6393                 }
6394                 if(tag == 'textarea'){
6395                     return w-2;
6396                 }
6397             }else if(Roo.isOpera){
6398                 if(tag == 'input'){
6399                     return w + 2;
6400                 }
6401                 if(tag == 'textarea'){
6402                     return w-2;
6403                 }
6404             }
6405         }
6406         return w;
6407     }
6408     
6409 });
6410
6411  
6412 /*
6413  * - LGPL
6414  *
6415  * Input
6416  * 
6417  */
6418
6419 /**
6420  * @class Roo.bootstrap.TextArea
6421  * @extends Roo.bootstrap.Input
6422  * Bootstrap TextArea class
6423  * @cfg {Number} cols Specifies the visible width of a text area
6424  * @cfg {Number} rows Specifies the visible number of lines in a text area
6425  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6426  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6427  * @cfg {string} html text
6428  * 
6429  * @constructor
6430  * Create a new TextArea
6431  * @param {Object} config The config object
6432  */
6433
6434 Roo.bootstrap.TextArea = function(config){
6435     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6436    
6437 };
6438
6439 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6440      
6441     cols : false,
6442     rows : 5,
6443     readOnly : false,
6444     warp : 'soft',
6445     resize : false,
6446     value: false,
6447     html: false,
6448     
6449     getAutoCreate : function(){
6450         
6451         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6452         
6453         var id = Roo.id();
6454         
6455         var cfg = {};
6456         
6457         var input =  {
6458             tag: 'textarea',
6459             id : id,
6460             warp : this.warp,
6461             rows : this.rows,
6462             value : this.value || '',
6463             html: this.html || '',
6464             cls : 'form-control',
6465             placeholder : this.placeholder || '' 
6466             
6467         };
6468         
6469         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6470             input.maxLength = this.maxLength;
6471         }
6472         
6473         if(this.resize){
6474             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6475         }
6476         
6477         if(this.cols){
6478             input.cols = this.cols;
6479         }
6480         
6481         if (this.readOnly) {
6482             input.readonly = true;
6483         }
6484         
6485         if (this.name) {
6486             input.name = this.name;
6487         }
6488         
6489         if (this.size) {
6490             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6491         }
6492         
6493         var settings=this;
6494         ['xs','sm','md','lg'].map(function(size){
6495             if (settings[size]) {
6496                 cfg.cls += ' col-' + size + '-' + settings[size];
6497             }
6498         });
6499         
6500         var inputblock = input;
6501         
6502         if (this.before || this.after) {
6503             
6504             inputblock = {
6505                 cls : 'input-group',
6506                 cn :  [] 
6507             };
6508             if (this.before) {
6509                 inputblock.cn.push({
6510                     tag :'span',
6511                     cls : 'input-group-addon',
6512                     html : this.before
6513                 });
6514             }
6515             inputblock.cn.push(input);
6516             if (this.after) {
6517                 inputblock.cn.push({
6518                     tag :'span',
6519                     cls : 'input-group-addon',
6520                     html : this.after
6521                 });
6522             }
6523             
6524         }
6525         
6526         if (align ==='left' && this.fieldLabel.length) {
6527                 Roo.log("left and has label");
6528                 cfg.cn = [
6529                     
6530                     {
6531                         tag: 'label',
6532                         'for' :  id,
6533                         cls : 'control-label col-sm-' + this.labelWidth,
6534                         html : this.fieldLabel
6535                         
6536                     },
6537                     {
6538                         cls : "col-sm-" + (12 - this.labelWidth), 
6539                         cn: [
6540                             inputblock
6541                         ]
6542                     }
6543                     
6544                 ];
6545         } else if ( this.fieldLabel.length) {
6546                 Roo.log(" label");
6547                  cfg.cn = [
6548                    
6549                     {
6550                         tag: 'label',
6551                         //cls : 'input-group-addon',
6552                         html : this.fieldLabel
6553                         
6554                     },
6555                     
6556                     inputblock
6557                     
6558                 ];
6559
6560         } else {
6561             
6562                    Roo.log(" no label && no align");
6563                 cfg.cn = [
6564                     
6565                         inputblock
6566                     
6567                 ];
6568                 
6569                 
6570         }
6571         
6572         if (this.disabled) {
6573             input.disabled=true;
6574         }
6575         
6576         return cfg;
6577         
6578     },
6579     /**
6580      * return the real textarea element.
6581      */
6582     inputEl: function ()
6583     {
6584         return this.el.select('textarea.form-control',true).first();
6585     }
6586 });
6587
6588  
6589 /*
6590  * - LGPL
6591  *
6592  * trigger field - base class for combo..
6593  * 
6594  */
6595  
6596 /**
6597  * @class Roo.bootstrap.TriggerField
6598  * @extends Roo.bootstrap.Input
6599  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6600  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6601  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6602  * for which you can provide a custom implementation.  For example:
6603  * <pre><code>
6604 var trigger = new Roo.bootstrap.TriggerField();
6605 trigger.onTriggerClick = myTriggerFn;
6606 trigger.applyTo('my-field');
6607 </code></pre>
6608  *
6609  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6610  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6611  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6612  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6613  * @constructor
6614  * Create a new TriggerField.
6615  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6616  * to the base TextField)
6617  */
6618 Roo.bootstrap.TriggerField = function(config){
6619     this.mimicing = false;
6620     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6621 };
6622
6623 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6624     /**
6625      * @cfg {String} triggerClass A CSS class to apply to the trigger
6626      */
6627      /**
6628      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6629      */
6630     hideTrigger:false,
6631
6632     /** @cfg {Boolean} grow @hide */
6633     /** @cfg {Number} growMin @hide */
6634     /** @cfg {Number} growMax @hide */
6635
6636     /**
6637      * @hide 
6638      * @method
6639      */
6640     autoSize: Roo.emptyFn,
6641     // private
6642     monitorTab : true,
6643     // private
6644     deferHeight : true,
6645
6646     
6647     actionMode : 'wrap',
6648     
6649     
6650     
6651     getAutoCreate : function(){
6652        
6653         var parent = this.parent();
6654         
6655         var align = this.labelAlign || this.parentLabelAlign();
6656         
6657         var id = Roo.id();
6658         
6659         var cfg = {
6660             cls: 'form-group' //input-group
6661         };
6662         
6663         
6664         var input =  {
6665             tag: 'input',
6666             id : id,
6667             type : this.inputType,
6668             cls : 'form-control',
6669             autocomplete: 'off',
6670             placeholder : this.placeholder || '' 
6671             
6672         };
6673         if (this.name) {
6674             input.name = this.name;
6675         }
6676         if (this.size) {
6677             input.cls += ' input-' + this.size;
6678         }
6679         
6680         if (this.disabled) {
6681             input.disabled=true;
6682         }
6683         
6684         var inputblock = input;
6685         
6686         if (this.before || this.after) {
6687             
6688             inputblock = {
6689                 cls : 'input-group',
6690                 cn :  [] 
6691             };
6692             if (this.before) {
6693                 inputblock.cn.push({
6694                     tag :'span',
6695                     cls : 'input-group-addon',
6696                     html : this.before
6697                 });
6698             }
6699             inputblock.cn.push(input);
6700             if (this.after) {
6701                 inputblock.cn.push({
6702                     tag :'span',
6703                     cls : 'input-group-addon',
6704                     html : this.after
6705                 });
6706             }
6707             
6708         };
6709         
6710         var box = {
6711             tag: 'div',
6712             cn: [
6713                 {
6714                     tag: 'input',
6715                     type : 'hidden',
6716                     cls: 'form-hidden-field'
6717                 },
6718                 inputblock
6719             ]
6720             
6721         };
6722         
6723         if(this.multiple){
6724             Roo.log('multiple');
6725             
6726             box = {
6727                 tag: 'div',
6728                 cn: [
6729                     {
6730                         tag: 'input',
6731                         type : 'hidden',
6732                         cls: 'form-hidden-field'
6733                     },
6734                     {
6735                         tag: 'ul',
6736                         cls: 'select2-choices',
6737                         cn:[
6738                             {
6739                                 tag: 'li',
6740                                 cls: 'select2-search-field',
6741                                 cn: [
6742
6743                                     inputblock
6744                                 ]
6745                             }
6746                         ]
6747                     }
6748                 ]
6749             }
6750         };
6751         
6752         var combobox = {
6753             cls: 'select2-container input-group',
6754             cn: [
6755                 box,
6756                 {
6757                     tag: 'ul',
6758                     cls: 'typeahead typeahead-long dropdown-menu',
6759                     style: 'display:none'
6760                 }
6761             ]
6762         };
6763         
6764         if(!this.multiple){
6765             combobox.cn.push({
6766                 tag :'span',
6767                 cls : 'input-group-addon btn dropdown-toggle',
6768                 cn : [
6769                     {
6770                         tag: 'span',
6771                         cls: 'caret'
6772                     },
6773                     {
6774                         tag: 'span',
6775                         cls: 'combobox-clear',
6776                         cn  : [
6777                             {
6778                                 tag : 'i',
6779                                 cls: 'icon-remove'
6780                             }
6781                         ]
6782                     }
6783                 ]
6784
6785             })
6786         }
6787         
6788         if(this.multiple){
6789             combobox.cls += ' select2-container-multi';
6790         }
6791         
6792         if (align ==='left' && this.fieldLabel.length) {
6793             
6794                 Roo.log("left and has label");
6795                 cfg.cn = [
6796                     
6797                     {
6798                         tag: 'label',
6799                         'for' :  id,
6800                         cls : 'control-label col-sm-' + this.labelWidth,
6801                         html : this.fieldLabel
6802                         
6803                     },
6804                     {
6805                         cls : "col-sm-" + (12 - this.labelWidth), 
6806                         cn: [
6807                             combobox
6808                         ]
6809                     }
6810                     
6811                 ];
6812         } else if ( this.fieldLabel.length) {
6813                 Roo.log(" label");
6814                  cfg.cn = [
6815                    
6816                     {
6817                         tag: 'label',
6818                         //cls : 'input-group-addon',
6819                         html : this.fieldLabel
6820                         
6821                     },
6822                     
6823                     combobox
6824                     
6825                 ];
6826
6827         } else {
6828             
6829                 Roo.log(" no label && no align");
6830                 cfg = combobox
6831                      
6832                 
6833         }
6834          
6835         var settings=this;
6836         ['xs','sm','md','lg'].map(function(size){
6837             if (settings[size]) {
6838                 cfg.cls += ' col-' + size + '-' + settings[size];
6839             }
6840         });
6841         
6842         return cfg;
6843         
6844     },
6845     
6846     
6847     
6848     // private
6849     onResize : function(w, h){
6850 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6851 //        if(typeof w == 'number'){
6852 //            var x = w - this.trigger.getWidth();
6853 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6854 //            this.trigger.setStyle('left', x+'px');
6855 //        }
6856     },
6857
6858     // private
6859     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6860
6861     // private
6862     getResizeEl : function(){
6863         return this.inputEl();
6864     },
6865
6866     // private
6867     getPositionEl : function(){
6868         return this.inputEl();
6869     },
6870
6871     // private
6872     alignErrorIcon : function(){
6873         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6874     },
6875
6876     // private
6877     initEvents : function(){
6878         
6879         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6880         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6881         if(!this.multiple){
6882             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6883             if(this.hideTrigger){
6884                 this.trigger.setDisplayed(false);
6885             }
6886             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6887         }
6888         
6889         if(this.multiple){
6890             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6891         }
6892         
6893         //this.trigger.addClassOnOver('x-form-trigger-over');
6894         //this.trigger.addClassOnClick('x-form-trigger-click');
6895         
6896         //if(!this.width){
6897         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6898         //}
6899     },
6900
6901     // private
6902     initTrigger : function(){
6903        
6904     },
6905
6906     // private
6907     onDestroy : function(){
6908         if(this.trigger){
6909             this.trigger.removeAllListeners();
6910           //  this.trigger.remove();
6911         }
6912         //if(this.wrap){
6913         //    this.wrap.remove();
6914         //}
6915         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6916     },
6917
6918     // private
6919     onFocus : function(){
6920         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6921         /*
6922         if(!this.mimicing){
6923             this.wrap.addClass('x-trigger-wrap-focus');
6924             this.mimicing = true;
6925             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6926             if(this.monitorTab){
6927                 this.el.on("keydown", this.checkTab, this);
6928             }
6929         }
6930         */
6931     },
6932
6933     // private
6934     checkTab : function(e){
6935         if(e.getKey() == e.TAB){
6936             this.triggerBlur();
6937         }
6938     },
6939
6940     // private
6941     onBlur : function(){
6942         // do nothing
6943     },
6944
6945     // private
6946     mimicBlur : function(e, t){
6947         /*
6948         if(!this.wrap.contains(t) && this.validateBlur()){
6949             this.triggerBlur();
6950         }
6951         */
6952     },
6953
6954     // private
6955     triggerBlur : function(){
6956         this.mimicing = false;
6957         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6958         if(this.monitorTab){
6959             this.el.un("keydown", this.checkTab, this);
6960         }
6961         //this.wrap.removeClass('x-trigger-wrap-focus');
6962         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6963     },
6964
6965     // private
6966     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6967     validateBlur : function(e, t){
6968         return true;
6969     },
6970
6971     // private
6972     onDisable : function(){
6973         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6974         //if(this.wrap){
6975         //    this.wrap.addClass('x-item-disabled');
6976         //}
6977     },
6978
6979     // private
6980     onEnable : function(){
6981         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6982         //if(this.wrap){
6983         //    this.el.removeClass('x-item-disabled');
6984         //}
6985     },
6986
6987     // private
6988     onShow : function(){
6989         var ae = this.getActionEl();
6990         
6991         if(ae){
6992             ae.dom.style.display = '';
6993             ae.dom.style.visibility = 'visible';
6994         }
6995     },
6996
6997     // private
6998     
6999     onHide : function(){
7000         var ae = this.getActionEl();
7001         ae.dom.style.display = 'none';
7002     },
7003
7004     /**
7005      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7006      * by an implementing function.
7007      * @method
7008      * @param {EventObject} e
7009      */
7010     onTriggerClick : Roo.emptyFn
7011 });
7012  /*
7013  * Based on:
7014  * Ext JS Library 1.1.1
7015  * Copyright(c) 2006-2007, Ext JS, LLC.
7016  *
7017  * Originally Released Under LGPL - original licence link has changed is not relivant.
7018  *
7019  * Fork - LGPL
7020  * <script type="text/javascript">
7021  */
7022
7023
7024 /**
7025  * @class Roo.data.SortTypes
7026  * @singleton
7027  * Defines the default sorting (casting?) comparison functions used when sorting data.
7028  */
7029 Roo.data.SortTypes = {
7030     /**
7031      * Default sort that does nothing
7032      * @param {Mixed} s The value being converted
7033      * @return {Mixed} The comparison value
7034      */
7035     none : function(s){
7036         return s;
7037     },
7038     
7039     /**
7040      * The regular expression used to strip tags
7041      * @type {RegExp}
7042      * @property
7043      */
7044     stripTagsRE : /<\/?[^>]+>/gi,
7045     
7046     /**
7047      * Strips all HTML tags to sort on text only
7048      * @param {Mixed} s The value being converted
7049      * @return {String} The comparison value
7050      */
7051     asText : function(s){
7052         return String(s).replace(this.stripTagsRE, "");
7053     },
7054     
7055     /**
7056      * Strips all HTML tags to sort on text only - Case insensitive
7057      * @param {Mixed} s The value being converted
7058      * @return {String} The comparison value
7059      */
7060     asUCText : function(s){
7061         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7062     },
7063     
7064     /**
7065      * Case insensitive string
7066      * @param {Mixed} s The value being converted
7067      * @return {String} The comparison value
7068      */
7069     asUCString : function(s) {
7070         return String(s).toUpperCase();
7071     },
7072     
7073     /**
7074      * Date sorting
7075      * @param {Mixed} s The value being converted
7076      * @return {Number} The comparison value
7077      */
7078     asDate : function(s) {
7079         if(!s){
7080             return 0;
7081         }
7082         if(s instanceof Date){
7083             return s.getTime();
7084         }
7085         return Date.parse(String(s));
7086     },
7087     
7088     /**
7089      * Float sorting
7090      * @param {Mixed} s The value being converted
7091      * @return {Float} The comparison value
7092      */
7093     asFloat : function(s) {
7094         var val = parseFloat(String(s).replace(/,/g, ""));
7095         if(isNaN(val)) val = 0;
7096         return val;
7097     },
7098     
7099     /**
7100      * Integer sorting
7101      * @param {Mixed} s The value being converted
7102      * @return {Number} The comparison value
7103      */
7104     asInt : function(s) {
7105         var val = parseInt(String(s).replace(/,/g, ""));
7106         if(isNaN(val)) val = 0;
7107         return val;
7108     }
7109 };/*
7110  * Based on:
7111  * Ext JS Library 1.1.1
7112  * Copyright(c) 2006-2007, Ext JS, LLC.
7113  *
7114  * Originally Released Under LGPL - original licence link has changed is not relivant.
7115  *
7116  * Fork - LGPL
7117  * <script type="text/javascript">
7118  */
7119
7120 /**
7121 * @class Roo.data.Record
7122  * Instances of this class encapsulate both record <em>definition</em> information, and record
7123  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7124  * to access Records cached in an {@link Roo.data.Store} object.<br>
7125  * <p>
7126  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7127  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7128  * objects.<br>
7129  * <p>
7130  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7131  * @constructor
7132  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7133  * {@link #create}. The parameters are the same.
7134  * @param {Array} data An associative Array of data values keyed by the field name.
7135  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7136  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7137  * not specified an integer id is generated.
7138  */
7139 Roo.data.Record = function(data, id){
7140     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7141     this.data = data;
7142 };
7143
7144 /**
7145  * Generate a constructor for a specific record layout.
7146  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7147  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7148  * Each field definition object may contain the following properties: <ul>
7149  * <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,
7150  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7151  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7152  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7153  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7154  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7155  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7156  * this may be omitted.</p></li>
7157  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7158  * <ul><li>auto (Default, implies no conversion)</li>
7159  * <li>string</li>
7160  * <li>int</li>
7161  * <li>float</li>
7162  * <li>boolean</li>
7163  * <li>date</li></ul></p></li>
7164  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7165  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7166  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7167  * by the Reader into an object that will be stored in the Record. It is passed the
7168  * following parameters:<ul>
7169  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7170  * </ul></p></li>
7171  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7172  * </ul>
7173  * <br>usage:<br><pre><code>
7174 var TopicRecord = Roo.data.Record.create(
7175     {name: 'title', mapping: 'topic_title'},
7176     {name: 'author', mapping: 'username'},
7177     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7178     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7179     {name: 'lastPoster', mapping: 'user2'},
7180     {name: 'excerpt', mapping: 'post_text'}
7181 );
7182
7183 var myNewRecord = new TopicRecord({
7184     title: 'Do my job please',
7185     author: 'noobie',
7186     totalPosts: 1,
7187     lastPost: new Date(),
7188     lastPoster: 'Animal',
7189     excerpt: 'No way dude!'
7190 });
7191 myStore.add(myNewRecord);
7192 </code></pre>
7193  * @method create
7194  * @static
7195  */
7196 Roo.data.Record.create = function(o){
7197     var f = function(){
7198         f.superclass.constructor.apply(this, arguments);
7199     };
7200     Roo.extend(f, Roo.data.Record);
7201     var p = f.prototype;
7202     p.fields = new Roo.util.MixedCollection(false, function(field){
7203         return field.name;
7204     });
7205     for(var i = 0, len = o.length; i < len; i++){
7206         p.fields.add(new Roo.data.Field(o[i]));
7207     }
7208     f.getField = function(name){
7209         return p.fields.get(name);  
7210     };
7211     return f;
7212 };
7213
7214 Roo.data.Record.AUTO_ID = 1000;
7215 Roo.data.Record.EDIT = 'edit';
7216 Roo.data.Record.REJECT = 'reject';
7217 Roo.data.Record.COMMIT = 'commit';
7218
7219 Roo.data.Record.prototype = {
7220     /**
7221      * Readonly flag - true if this record has been modified.
7222      * @type Boolean
7223      */
7224     dirty : false,
7225     editing : false,
7226     error: null,
7227     modified: null,
7228
7229     // private
7230     join : function(store){
7231         this.store = store;
7232     },
7233
7234     /**
7235      * Set the named field to the specified value.
7236      * @param {String} name The name of the field to set.
7237      * @param {Object} value The value to set the field to.
7238      */
7239     set : function(name, value){
7240         if(this.data[name] == value){
7241             return;
7242         }
7243         this.dirty = true;
7244         if(!this.modified){
7245             this.modified = {};
7246         }
7247         if(typeof this.modified[name] == 'undefined'){
7248             this.modified[name] = this.data[name];
7249         }
7250         this.data[name] = value;
7251         if(!this.editing && this.store){
7252             this.store.afterEdit(this);
7253         }       
7254     },
7255
7256     /**
7257      * Get the value of the named field.
7258      * @param {String} name The name of the field to get the value of.
7259      * @return {Object} The value of the field.
7260      */
7261     get : function(name){
7262         return this.data[name]; 
7263     },
7264
7265     // private
7266     beginEdit : function(){
7267         this.editing = true;
7268         this.modified = {}; 
7269     },
7270
7271     // private
7272     cancelEdit : function(){
7273         this.editing = false;
7274         delete this.modified;
7275     },
7276
7277     // private
7278     endEdit : function(){
7279         this.editing = false;
7280         if(this.dirty && this.store){
7281             this.store.afterEdit(this);
7282         }
7283     },
7284
7285     /**
7286      * Usually called by the {@link Roo.data.Store} which owns the Record.
7287      * Rejects all changes made to the Record since either creation, or the last commit operation.
7288      * Modified fields are reverted to their original values.
7289      * <p>
7290      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7291      * of reject operations.
7292      */
7293     reject : function(){
7294         var m = this.modified;
7295         for(var n in m){
7296             if(typeof m[n] != "function"){
7297                 this.data[n] = m[n];
7298             }
7299         }
7300         this.dirty = false;
7301         delete this.modified;
7302         this.editing = false;
7303         if(this.store){
7304             this.store.afterReject(this);
7305         }
7306     },
7307
7308     /**
7309      * Usually called by the {@link Roo.data.Store} which owns the Record.
7310      * Commits all changes made to the Record since either creation, or the last commit operation.
7311      * <p>
7312      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7313      * of commit operations.
7314      */
7315     commit : function(){
7316         this.dirty = false;
7317         delete this.modified;
7318         this.editing = false;
7319         if(this.store){
7320             this.store.afterCommit(this);
7321         }
7322     },
7323
7324     // private
7325     hasError : function(){
7326         return this.error != null;
7327     },
7328
7329     // private
7330     clearError : function(){
7331         this.error = null;
7332     },
7333
7334     /**
7335      * Creates a copy of this record.
7336      * @param {String} id (optional) A new record id if you don't want to use this record's id
7337      * @return {Record}
7338      */
7339     copy : function(newId) {
7340         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7341     }
7342 };/*
7343  * Based on:
7344  * Ext JS Library 1.1.1
7345  * Copyright(c) 2006-2007, Ext JS, LLC.
7346  *
7347  * Originally Released Under LGPL - original licence link has changed is not relivant.
7348  *
7349  * Fork - LGPL
7350  * <script type="text/javascript">
7351  */
7352
7353
7354
7355 /**
7356  * @class Roo.data.Store
7357  * @extends Roo.util.Observable
7358  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7359  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7360  * <p>
7361  * 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
7362  * has no knowledge of the format of the data returned by the Proxy.<br>
7363  * <p>
7364  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7365  * instances from the data object. These records are cached and made available through accessor functions.
7366  * @constructor
7367  * Creates a new Store.
7368  * @param {Object} config A config object containing the objects needed for the Store to access data,
7369  * and read the data into Records.
7370  */
7371 Roo.data.Store = function(config){
7372     this.data = new Roo.util.MixedCollection(false);
7373     this.data.getKey = function(o){
7374         return o.id;
7375     };
7376     this.baseParams = {};
7377     // private
7378     this.paramNames = {
7379         "start" : "start",
7380         "limit" : "limit",
7381         "sort" : "sort",
7382         "dir" : "dir",
7383         "multisort" : "_multisort"
7384     };
7385
7386     if(config && config.data){
7387         this.inlineData = config.data;
7388         delete config.data;
7389     }
7390
7391     Roo.apply(this, config);
7392     
7393     if(this.reader){ // reader passed
7394         this.reader = Roo.factory(this.reader, Roo.data);
7395         this.reader.xmodule = this.xmodule || false;
7396         if(!this.recordType){
7397             this.recordType = this.reader.recordType;
7398         }
7399         if(this.reader.onMetaChange){
7400             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7401         }
7402     }
7403
7404     if(this.recordType){
7405         this.fields = this.recordType.prototype.fields;
7406     }
7407     this.modified = [];
7408
7409     this.addEvents({
7410         /**
7411          * @event datachanged
7412          * Fires when the data cache has changed, and a widget which is using this Store
7413          * as a Record cache should refresh its view.
7414          * @param {Store} this
7415          */
7416         datachanged : true,
7417         /**
7418          * @event metachange
7419          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7420          * @param {Store} this
7421          * @param {Object} meta The JSON metadata
7422          */
7423         metachange : true,
7424         /**
7425          * @event add
7426          * Fires when Records have been added to the Store
7427          * @param {Store} this
7428          * @param {Roo.data.Record[]} records The array of Records added
7429          * @param {Number} index The index at which the record(s) were added
7430          */
7431         add : true,
7432         /**
7433          * @event remove
7434          * Fires when a Record has been removed from the Store
7435          * @param {Store} this
7436          * @param {Roo.data.Record} record The Record that was removed
7437          * @param {Number} index The index at which the record was removed
7438          */
7439         remove : true,
7440         /**
7441          * @event update
7442          * Fires when a Record has been updated
7443          * @param {Store} this
7444          * @param {Roo.data.Record} record The Record that was updated
7445          * @param {String} operation The update operation being performed.  Value may be one of:
7446          * <pre><code>
7447  Roo.data.Record.EDIT
7448  Roo.data.Record.REJECT
7449  Roo.data.Record.COMMIT
7450          * </code></pre>
7451          */
7452         update : true,
7453         /**
7454          * @event clear
7455          * Fires when the data cache has been cleared.
7456          * @param {Store} this
7457          */
7458         clear : true,
7459         /**
7460          * @event beforeload
7461          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7462          * the load action will be canceled.
7463          * @param {Store} this
7464          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7465          */
7466         beforeload : true,
7467         /**
7468          * @event beforeloadadd
7469          * Fires after a new set of Records has been loaded.
7470          * @param {Store} this
7471          * @param {Roo.data.Record[]} records The Records that were loaded
7472          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7473          */
7474         beforeloadadd : true,
7475         /**
7476          * @event load
7477          * Fires after a new set of Records has been loaded, before they are added to the store.
7478          * @param {Store} this
7479          * @param {Roo.data.Record[]} records The Records that were loaded
7480          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7481          * @params {Object} return from reader
7482          */
7483         load : true,
7484         /**
7485          * @event loadexception
7486          * Fires if an exception occurs in the Proxy during loading.
7487          * Called with the signature of the Proxy's "loadexception" event.
7488          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7489          * 
7490          * @param {Proxy} 
7491          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7492          * @param {Object} load options 
7493          * @param {Object} jsonData from your request (normally this contains the Exception)
7494          */
7495         loadexception : true
7496     });
7497     
7498     if(this.proxy){
7499         this.proxy = Roo.factory(this.proxy, Roo.data);
7500         this.proxy.xmodule = this.xmodule || false;
7501         this.relayEvents(this.proxy,  ["loadexception"]);
7502     }
7503     this.sortToggle = {};
7504     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7505
7506     Roo.data.Store.superclass.constructor.call(this);
7507
7508     if(this.inlineData){
7509         this.loadData(this.inlineData);
7510         delete this.inlineData;
7511     }
7512 };
7513
7514 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7515      /**
7516     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7517     * without a remote query - used by combo/forms at present.
7518     */
7519     
7520     /**
7521     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7522     */
7523     /**
7524     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7525     */
7526     /**
7527     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7528     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7529     */
7530     /**
7531     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7532     * on any HTTP request
7533     */
7534     /**
7535     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7536     */
7537     /**
7538     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7539     */
7540     multiSort: false,
7541     /**
7542     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7543     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7544     */
7545     remoteSort : false,
7546
7547     /**
7548     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7549      * loaded or when a record is removed. (defaults to false).
7550     */
7551     pruneModifiedRecords : false,
7552
7553     // private
7554     lastOptions : null,
7555
7556     /**
7557      * Add Records to the Store and fires the add event.
7558      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7559      */
7560     add : function(records){
7561         records = [].concat(records);
7562         for(var i = 0, len = records.length; i < len; i++){
7563             records[i].join(this);
7564         }
7565         var index = this.data.length;
7566         this.data.addAll(records);
7567         this.fireEvent("add", this, records, index);
7568     },
7569
7570     /**
7571      * Remove a Record from the Store and fires the remove event.
7572      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7573      */
7574     remove : function(record){
7575         var index = this.data.indexOf(record);
7576         this.data.removeAt(index);
7577         if(this.pruneModifiedRecords){
7578             this.modified.remove(record);
7579         }
7580         this.fireEvent("remove", this, record, index);
7581     },
7582
7583     /**
7584      * Remove all Records from the Store and fires the clear event.
7585      */
7586     removeAll : function(){
7587         this.data.clear();
7588         if(this.pruneModifiedRecords){
7589             this.modified = [];
7590         }
7591         this.fireEvent("clear", this);
7592     },
7593
7594     /**
7595      * Inserts Records to the Store at the given index and fires the add event.
7596      * @param {Number} index The start index at which to insert the passed Records.
7597      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7598      */
7599     insert : function(index, records){
7600         records = [].concat(records);
7601         for(var i = 0, len = records.length; i < len; i++){
7602             this.data.insert(index, records[i]);
7603             records[i].join(this);
7604         }
7605         this.fireEvent("add", this, records, index);
7606     },
7607
7608     /**
7609      * Get the index within the cache of the passed Record.
7610      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7611      * @return {Number} The index of the passed Record. Returns -1 if not found.
7612      */
7613     indexOf : function(record){
7614         return this.data.indexOf(record);
7615     },
7616
7617     /**
7618      * Get the index within the cache of the Record with the passed id.
7619      * @param {String} id The id of the Record to find.
7620      * @return {Number} The index of the Record. Returns -1 if not found.
7621      */
7622     indexOfId : function(id){
7623         return this.data.indexOfKey(id);
7624     },
7625
7626     /**
7627      * Get the Record with the specified id.
7628      * @param {String} id The id of the Record to find.
7629      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7630      */
7631     getById : function(id){
7632         return this.data.key(id);
7633     },
7634
7635     /**
7636      * Get the Record at the specified index.
7637      * @param {Number} index The index of the Record to find.
7638      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7639      */
7640     getAt : function(index){
7641         return this.data.itemAt(index);
7642     },
7643
7644     /**
7645      * Returns a range of Records between specified indices.
7646      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7647      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7648      * @return {Roo.data.Record[]} An array of Records
7649      */
7650     getRange : function(start, end){
7651         return this.data.getRange(start, end);
7652     },
7653
7654     // private
7655     storeOptions : function(o){
7656         o = Roo.apply({}, o);
7657         delete o.callback;
7658         delete o.scope;
7659         this.lastOptions = o;
7660     },
7661
7662     /**
7663      * Loads the Record cache from the configured Proxy using the configured Reader.
7664      * <p>
7665      * If using remote paging, then the first load call must specify the <em>start</em>
7666      * and <em>limit</em> properties in the options.params property to establish the initial
7667      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7668      * <p>
7669      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7670      * and this call will return before the new data has been loaded. Perform any post-processing
7671      * in a callback function, or in a "load" event handler.</strong>
7672      * <p>
7673      * @param {Object} options An object containing properties which control loading options:<ul>
7674      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7675      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7676      * passed the following arguments:<ul>
7677      * <li>r : Roo.data.Record[]</li>
7678      * <li>options: Options object from the load call</li>
7679      * <li>success: Boolean success indicator</li></ul></li>
7680      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7681      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7682      * </ul>
7683      */
7684     load : function(options){
7685         options = options || {};
7686         if(this.fireEvent("beforeload", this, options) !== false){
7687             this.storeOptions(options);
7688             var p = Roo.apply(options.params || {}, this.baseParams);
7689             // if meta was not loaded from remote source.. try requesting it.
7690             if (!this.reader.metaFromRemote) {
7691                 p._requestMeta = 1;
7692             }
7693             if(this.sortInfo && this.remoteSort){
7694                 var pn = this.paramNames;
7695                 p[pn["sort"]] = this.sortInfo.field;
7696                 p[pn["dir"]] = this.sortInfo.direction;
7697             }
7698             if (this.multiSort) {
7699                 var pn = this.paramNames;
7700                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7701             }
7702             
7703             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7704         }
7705     },
7706
7707     /**
7708      * Reloads the Record cache from the configured Proxy using the configured Reader and
7709      * the options from the last load operation performed.
7710      * @param {Object} options (optional) An object containing properties which may override the options
7711      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7712      * the most recently used options are reused).
7713      */
7714     reload : function(options){
7715         this.load(Roo.applyIf(options||{}, this.lastOptions));
7716     },
7717
7718     // private
7719     // Called as a callback by the Reader during a load operation.
7720     loadRecords : function(o, options, success){
7721         if(!o || success === false){
7722             if(success !== false){
7723                 this.fireEvent("load", this, [], options, o);
7724             }
7725             if(options.callback){
7726                 options.callback.call(options.scope || this, [], options, false);
7727             }
7728             return;
7729         }
7730         // if data returned failure - throw an exception.
7731         if (o.success === false) {
7732             // show a message if no listener is registered.
7733             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7734                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7735             }
7736             // loadmask wil be hooked into this..
7737             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7738             return;
7739         }
7740         var r = o.records, t = o.totalRecords || r.length;
7741         
7742         this.fireEvent("beforeloadadd", this, r, options, o);
7743         
7744         if(!options || options.add !== true){
7745             if(this.pruneModifiedRecords){
7746                 this.modified = [];
7747             }
7748             for(var i = 0, len = r.length; i < len; i++){
7749                 r[i].join(this);
7750             }
7751             if(this.snapshot){
7752                 this.data = this.snapshot;
7753                 delete this.snapshot;
7754             }
7755             this.data.clear();
7756             this.data.addAll(r);
7757             this.totalLength = t;
7758             this.applySort();
7759             this.fireEvent("datachanged", this);
7760         }else{
7761             this.totalLength = Math.max(t, this.data.length+r.length);
7762             this.add(r);
7763         }
7764         this.fireEvent("load", this, r, options, o);
7765         if(options.callback){
7766             options.callback.call(options.scope || this, r, options, true);
7767         }
7768     },
7769
7770
7771     /**
7772      * Loads data from a passed data block. A Reader which understands the format of the data
7773      * must have been configured in the constructor.
7774      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7775      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7776      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7777      */
7778     loadData : function(o, append){
7779         var r = this.reader.readRecords(o);
7780         this.loadRecords(r, {add: append}, true);
7781     },
7782
7783     /**
7784      * Gets the number of cached records.
7785      * <p>
7786      * <em>If using paging, this may not be the total size of the dataset. If the data object
7787      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7788      * the data set size</em>
7789      */
7790     getCount : function(){
7791         return this.data.length || 0;
7792     },
7793
7794     /**
7795      * Gets the total number of records in the dataset as returned by the server.
7796      * <p>
7797      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7798      * the dataset size</em>
7799      */
7800     getTotalCount : function(){
7801         return this.totalLength || 0;
7802     },
7803
7804     /**
7805      * Returns the sort state of the Store as an object with two properties:
7806      * <pre><code>
7807  field {String} The name of the field by which the Records are sorted
7808  direction {String} The sort order, "ASC" or "DESC"
7809      * </code></pre>
7810      */
7811     getSortState : function(){
7812         return this.sortInfo;
7813     },
7814
7815     // private
7816     applySort : function(){
7817         if(this.sortInfo && !this.remoteSort){
7818             var s = this.sortInfo, f = s.field;
7819             var st = this.fields.get(f).sortType;
7820             var fn = function(r1, r2){
7821                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7822                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7823             };
7824             this.data.sort(s.direction, fn);
7825             if(this.snapshot && this.snapshot != this.data){
7826                 this.snapshot.sort(s.direction, fn);
7827             }
7828         }
7829     },
7830
7831     /**
7832      * Sets the default sort column and order to be used by the next load operation.
7833      * @param {String} fieldName The name of the field to sort by.
7834      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7835      */
7836     setDefaultSort : function(field, dir){
7837         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7838     },
7839
7840     /**
7841      * Sort the Records.
7842      * If remote sorting is used, the sort is performed on the server, and the cache is
7843      * reloaded. If local sorting is used, the cache is sorted internally.
7844      * @param {String} fieldName The name of the field to sort by.
7845      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7846      */
7847     sort : function(fieldName, dir){
7848         var f = this.fields.get(fieldName);
7849         if(!dir){
7850             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7851             
7852             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7853                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7854             }else{
7855                 dir = f.sortDir;
7856             }
7857         }
7858         this.sortToggle[f.name] = dir;
7859         this.sortInfo = {field: f.name, direction: dir};
7860         if(!this.remoteSort){
7861             this.applySort();
7862             this.fireEvent("datachanged", this);
7863         }else{
7864             this.load(this.lastOptions);
7865         }
7866     },
7867
7868     /**
7869      * Calls the specified function for each of the Records in the cache.
7870      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7871      * Returning <em>false</em> aborts and exits the iteration.
7872      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7873      */
7874     each : function(fn, scope){
7875         this.data.each(fn, scope);
7876     },
7877
7878     /**
7879      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7880      * (e.g., during paging).
7881      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7882      */
7883     getModifiedRecords : function(){
7884         return this.modified;
7885     },
7886
7887     // private
7888     createFilterFn : function(property, value, anyMatch){
7889         if(!value.exec){ // not a regex
7890             value = String(value);
7891             if(value.length == 0){
7892                 return false;
7893             }
7894             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7895         }
7896         return function(r){
7897             return value.test(r.data[property]);
7898         };
7899     },
7900
7901     /**
7902      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7903      * @param {String} property A field on your records
7904      * @param {Number} start The record index to start at (defaults to 0)
7905      * @param {Number} end The last record index to include (defaults to length - 1)
7906      * @return {Number} The sum
7907      */
7908     sum : function(property, start, end){
7909         var rs = this.data.items, v = 0;
7910         start = start || 0;
7911         end = (end || end === 0) ? end : rs.length-1;
7912
7913         for(var i = start; i <= end; i++){
7914             v += (rs[i].data[property] || 0);
7915         }
7916         return v;
7917     },
7918
7919     /**
7920      * Filter the records by a specified property.
7921      * @param {String} field A field on your records
7922      * @param {String/RegExp} value Either a string that the field
7923      * should start with or a RegExp to test against the field
7924      * @param {Boolean} anyMatch True to match any part not just the beginning
7925      */
7926     filter : function(property, value, anyMatch){
7927         var fn = this.createFilterFn(property, value, anyMatch);
7928         return fn ? this.filterBy(fn) : this.clearFilter();
7929     },
7930
7931     /**
7932      * Filter by a function. The specified function will be called with each
7933      * record in this data source. If the function returns true the record is included,
7934      * otherwise it is filtered.
7935      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7936      * @param {Object} scope (optional) The scope of the function (defaults to this)
7937      */
7938     filterBy : function(fn, scope){
7939         this.snapshot = this.snapshot || this.data;
7940         this.data = this.queryBy(fn, scope||this);
7941         this.fireEvent("datachanged", this);
7942     },
7943
7944     /**
7945      * Query the records by a specified property.
7946      * @param {String} field A field on your records
7947      * @param {String/RegExp} value Either a string that the field
7948      * should start with or a RegExp to test against the field
7949      * @param {Boolean} anyMatch True to match any part not just the beginning
7950      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7951      */
7952     query : function(property, value, anyMatch){
7953         var fn = this.createFilterFn(property, value, anyMatch);
7954         return fn ? this.queryBy(fn) : this.data.clone();
7955     },
7956
7957     /**
7958      * Query by a function. The specified function will be called with each
7959      * record in this data source. If the function returns true the record is included
7960      * in the results.
7961      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7962      * @param {Object} scope (optional) The scope of the function (defaults to this)
7963       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7964      **/
7965     queryBy : function(fn, scope){
7966         var data = this.snapshot || this.data;
7967         return data.filterBy(fn, scope||this);
7968     },
7969
7970     /**
7971      * Collects unique values for a particular dataIndex from this store.
7972      * @param {String} dataIndex The property to collect
7973      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7974      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7975      * @return {Array} An array of the unique values
7976      **/
7977     collect : function(dataIndex, allowNull, bypassFilter){
7978         var d = (bypassFilter === true && this.snapshot) ?
7979                 this.snapshot.items : this.data.items;
7980         var v, sv, r = [], l = {};
7981         for(var i = 0, len = d.length; i < len; i++){
7982             v = d[i].data[dataIndex];
7983             sv = String(v);
7984             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7985                 l[sv] = true;
7986                 r[r.length] = v;
7987             }
7988         }
7989         return r;
7990     },
7991
7992     /**
7993      * Revert to a view of the Record cache with no filtering applied.
7994      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7995      */
7996     clearFilter : function(suppressEvent){
7997         if(this.snapshot && this.snapshot != this.data){
7998             this.data = this.snapshot;
7999             delete this.snapshot;
8000             if(suppressEvent !== true){
8001                 this.fireEvent("datachanged", this);
8002             }
8003         }
8004     },
8005
8006     // private
8007     afterEdit : function(record){
8008         if(this.modified.indexOf(record) == -1){
8009             this.modified.push(record);
8010         }
8011         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8012     },
8013     
8014     // private
8015     afterReject : function(record){
8016         this.modified.remove(record);
8017         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8018     },
8019
8020     // private
8021     afterCommit : function(record){
8022         this.modified.remove(record);
8023         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8024     },
8025
8026     /**
8027      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8028      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8029      */
8030     commitChanges : function(){
8031         var m = this.modified.slice(0);
8032         this.modified = [];
8033         for(var i = 0, len = m.length; i < len; i++){
8034             m[i].commit();
8035         }
8036     },
8037
8038     /**
8039      * Cancel outstanding changes on all changed records.
8040      */
8041     rejectChanges : function(){
8042         var m = this.modified.slice(0);
8043         this.modified = [];
8044         for(var i = 0, len = m.length; i < len; i++){
8045             m[i].reject();
8046         }
8047     },
8048
8049     onMetaChange : function(meta, rtype, o){
8050         this.recordType = rtype;
8051         this.fields = rtype.prototype.fields;
8052         delete this.snapshot;
8053         this.sortInfo = meta.sortInfo || this.sortInfo;
8054         this.modified = [];
8055         this.fireEvent('metachange', this, this.reader.meta);
8056     },
8057     
8058     moveIndex : function(data, type)
8059     {
8060         var index = this.indexOf(data);
8061         
8062         var newIndex = index + type;
8063         
8064         this.remove(data);
8065         
8066         this.insert(newIndex, data);
8067         
8068     }
8069 });/*
8070  * Based on:
8071  * Ext JS Library 1.1.1
8072  * Copyright(c) 2006-2007, Ext JS, LLC.
8073  *
8074  * Originally Released Under LGPL - original licence link has changed is not relivant.
8075  *
8076  * Fork - LGPL
8077  * <script type="text/javascript">
8078  */
8079
8080 /**
8081  * @class Roo.data.SimpleStore
8082  * @extends Roo.data.Store
8083  * Small helper class to make creating Stores from Array data easier.
8084  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8085  * @cfg {Array} fields An array of field definition objects, or field name strings.
8086  * @cfg {Array} data The multi-dimensional array of data
8087  * @constructor
8088  * @param {Object} config
8089  */
8090 Roo.data.SimpleStore = function(config){
8091     Roo.data.SimpleStore.superclass.constructor.call(this, {
8092         isLocal : true,
8093         reader: new Roo.data.ArrayReader({
8094                 id: config.id
8095             },
8096             Roo.data.Record.create(config.fields)
8097         ),
8098         proxy : new Roo.data.MemoryProxy(config.data)
8099     });
8100     this.load();
8101 };
8102 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8103  * Based on:
8104  * Ext JS Library 1.1.1
8105  * Copyright(c) 2006-2007, Ext JS, LLC.
8106  *
8107  * Originally Released Under LGPL - original licence link has changed is not relivant.
8108  *
8109  * Fork - LGPL
8110  * <script type="text/javascript">
8111  */
8112
8113 /**
8114 /**
8115  * @extends Roo.data.Store
8116  * @class Roo.data.JsonStore
8117  * Small helper class to make creating Stores for JSON data easier. <br/>
8118 <pre><code>
8119 var store = new Roo.data.JsonStore({
8120     url: 'get-images.php',
8121     root: 'images',
8122     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8123 });
8124 </code></pre>
8125  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8126  * JsonReader and HttpProxy (unless inline data is provided).</b>
8127  * @cfg {Array} fields An array of field definition objects, or field name strings.
8128  * @constructor
8129  * @param {Object} config
8130  */
8131 Roo.data.JsonStore = function(c){
8132     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8133         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8134         reader: new Roo.data.JsonReader(c, c.fields)
8135     }));
8136 };
8137 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8138  * Based on:
8139  * Ext JS Library 1.1.1
8140  * Copyright(c) 2006-2007, Ext JS, LLC.
8141  *
8142  * Originally Released Under LGPL - original licence link has changed is not relivant.
8143  *
8144  * Fork - LGPL
8145  * <script type="text/javascript">
8146  */
8147
8148  
8149 Roo.data.Field = function(config){
8150     if(typeof config == "string"){
8151         config = {name: config};
8152     }
8153     Roo.apply(this, config);
8154     
8155     if(!this.type){
8156         this.type = "auto";
8157     }
8158     
8159     var st = Roo.data.SortTypes;
8160     // named sortTypes are supported, here we look them up
8161     if(typeof this.sortType == "string"){
8162         this.sortType = st[this.sortType];
8163     }
8164     
8165     // set default sortType for strings and dates
8166     if(!this.sortType){
8167         switch(this.type){
8168             case "string":
8169                 this.sortType = st.asUCString;
8170                 break;
8171             case "date":
8172                 this.sortType = st.asDate;
8173                 break;
8174             default:
8175                 this.sortType = st.none;
8176         }
8177     }
8178
8179     // define once
8180     var stripRe = /[\$,%]/g;
8181
8182     // prebuilt conversion function for this field, instead of
8183     // switching every time we're reading a value
8184     if(!this.convert){
8185         var cv, dateFormat = this.dateFormat;
8186         switch(this.type){
8187             case "":
8188             case "auto":
8189             case undefined:
8190                 cv = function(v){ return v; };
8191                 break;
8192             case "string":
8193                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8194                 break;
8195             case "int":
8196                 cv = function(v){
8197                     return v !== undefined && v !== null && v !== '' ?
8198                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8199                     };
8200                 break;
8201             case "float":
8202                 cv = function(v){
8203                     return v !== undefined && v !== null && v !== '' ?
8204                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8205                     };
8206                 break;
8207             case "bool":
8208             case "boolean":
8209                 cv = function(v){ return v === true || v === "true" || v == 1; };
8210                 break;
8211             case "date":
8212                 cv = function(v){
8213                     if(!v){
8214                         return '';
8215                     }
8216                     if(v instanceof Date){
8217                         return v;
8218                     }
8219                     if(dateFormat){
8220                         if(dateFormat == "timestamp"){
8221                             return new Date(v*1000);
8222                         }
8223                         return Date.parseDate(v, dateFormat);
8224                     }
8225                     var parsed = Date.parse(v);
8226                     return parsed ? new Date(parsed) : null;
8227                 };
8228              break;
8229             
8230         }
8231         this.convert = cv;
8232     }
8233 };
8234
8235 Roo.data.Field.prototype = {
8236     dateFormat: null,
8237     defaultValue: "",
8238     mapping: null,
8239     sortType : null,
8240     sortDir : "ASC"
8241 };/*
8242  * Based on:
8243  * Ext JS Library 1.1.1
8244  * Copyright(c) 2006-2007, Ext JS, LLC.
8245  *
8246  * Originally Released Under LGPL - original licence link has changed is not relivant.
8247  *
8248  * Fork - LGPL
8249  * <script type="text/javascript">
8250  */
8251  
8252 // Base class for reading structured data from a data source.  This class is intended to be
8253 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8254
8255 /**
8256  * @class Roo.data.DataReader
8257  * Base class for reading structured data from a data source.  This class is intended to be
8258  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8259  */
8260
8261 Roo.data.DataReader = function(meta, recordType){
8262     
8263     this.meta = meta;
8264     
8265     this.recordType = recordType instanceof Array ? 
8266         Roo.data.Record.create(recordType) : recordType;
8267 };
8268
8269 Roo.data.DataReader.prototype = {
8270      /**
8271      * Create an empty record
8272      * @param {Object} data (optional) - overlay some values
8273      * @return {Roo.data.Record} record created.
8274      */
8275     newRow :  function(d) {
8276         var da =  {};
8277         this.recordType.prototype.fields.each(function(c) {
8278             switch( c.type) {
8279                 case 'int' : da[c.name] = 0; break;
8280                 case 'date' : da[c.name] = new Date(); break;
8281                 case 'float' : da[c.name] = 0.0; break;
8282                 case 'boolean' : da[c.name] = false; break;
8283                 default : da[c.name] = ""; break;
8284             }
8285             
8286         });
8287         return new this.recordType(Roo.apply(da, d));
8288     }
8289     
8290 };/*
8291  * Based on:
8292  * Ext JS Library 1.1.1
8293  * Copyright(c) 2006-2007, Ext JS, LLC.
8294  *
8295  * Originally Released Under LGPL - original licence link has changed is not relivant.
8296  *
8297  * Fork - LGPL
8298  * <script type="text/javascript">
8299  */
8300
8301 /**
8302  * @class Roo.data.DataProxy
8303  * @extends Roo.data.Observable
8304  * This class is an abstract base class for implementations which provide retrieval of
8305  * unformatted data objects.<br>
8306  * <p>
8307  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8308  * (of the appropriate type which knows how to parse the data object) to provide a block of
8309  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8310  * <p>
8311  * Custom implementations must implement the load method as described in
8312  * {@link Roo.data.HttpProxy#load}.
8313  */
8314 Roo.data.DataProxy = function(){
8315     this.addEvents({
8316         /**
8317          * @event beforeload
8318          * Fires before a network request is made to retrieve a data object.
8319          * @param {Object} This DataProxy object.
8320          * @param {Object} params The params parameter to the load function.
8321          */
8322         beforeload : true,
8323         /**
8324          * @event load
8325          * Fires before the load method's callback is called.
8326          * @param {Object} This DataProxy object.
8327          * @param {Object} o The data object.
8328          * @param {Object} arg The callback argument object passed to the load function.
8329          */
8330         load : true,
8331         /**
8332          * @event loadexception
8333          * Fires if an Exception occurs during data retrieval.
8334          * @param {Object} This DataProxy object.
8335          * @param {Object} o The data object.
8336          * @param {Object} arg The callback argument object passed to the load function.
8337          * @param {Object} e The Exception.
8338          */
8339         loadexception : true
8340     });
8341     Roo.data.DataProxy.superclass.constructor.call(this);
8342 };
8343
8344 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8345
8346     /**
8347      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8348      */
8349 /*
8350  * Based on:
8351  * Ext JS Library 1.1.1
8352  * Copyright(c) 2006-2007, Ext JS, LLC.
8353  *
8354  * Originally Released Under LGPL - original licence link has changed is not relivant.
8355  *
8356  * Fork - LGPL
8357  * <script type="text/javascript">
8358  */
8359 /**
8360  * @class Roo.data.MemoryProxy
8361  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8362  * to the Reader when its load method is called.
8363  * @constructor
8364  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8365  */
8366 Roo.data.MemoryProxy = function(data){
8367     if (data.data) {
8368         data = data.data;
8369     }
8370     Roo.data.MemoryProxy.superclass.constructor.call(this);
8371     this.data = data;
8372 };
8373
8374 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8375     /**
8376      * Load data from the requested source (in this case an in-memory
8377      * data object passed to the constructor), read the data object into
8378      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8379      * process that block using the passed callback.
8380      * @param {Object} params This parameter is not used by the MemoryProxy class.
8381      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8382      * object into a block of Roo.data.Records.
8383      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8384      * The function must be passed <ul>
8385      * <li>The Record block object</li>
8386      * <li>The "arg" argument from the load function</li>
8387      * <li>A boolean success indicator</li>
8388      * </ul>
8389      * @param {Object} scope The scope in which to call the callback
8390      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8391      */
8392     load : function(params, reader, callback, scope, arg){
8393         params = params || {};
8394         var result;
8395         try {
8396             result = reader.readRecords(this.data);
8397         }catch(e){
8398             this.fireEvent("loadexception", this, arg, null, e);
8399             callback.call(scope, null, arg, false);
8400             return;
8401         }
8402         callback.call(scope, result, arg, true);
8403     },
8404     
8405     // private
8406     update : function(params, records){
8407         
8408     }
8409 });/*
8410  * Based on:
8411  * Ext JS Library 1.1.1
8412  * Copyright(c) 2006-2007, Ext JS, LLC.
8413  *
8414  * Originally Released Under LGPL - original licence link has changed is not relivant.
8415  *
8416  * Fork - LGPL
8417  * <script type="text/javascript">
8418  */
8419 /**
8420  * @class Roo.data.HttpProxy
8421  * @extends Roo.data.DataProxy
8422  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8423  * configured to reference a certain URL.<br><br>
8424  * <p>
8425  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8426  * from which the running page was served.<br><br>
8427  * <p>
8428  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8429  * <p>
8430  * Be aware that to enable the browser to parse an XML document, the server must set
8431  * the Content-Type header in the HTTP response to "text/xml".
8432  * @constructor
8433  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8434  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8435  * will be used to make the request.
8436  */
8437 Roo.data.HttpProxy = function(conn){
8438     Roo.data.HttpProxy.superclass.constructor.call(this);
8439     // is conn a conn config or a real conn?
8440     this.conn = conn;
8441     this.useAjax = !conn || !conn.events;
8442   
8443 };
8444
8445 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8446     // thse are take from connection...
8447     
8448     /**
8449      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8450      */
8451     /**
8452      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8453      * extra parameters to each request made by this object. (defaults to undefined)
8454      */
8455     /**
8456      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8457      *  to each request made by this object. (defaults to undefined)
8458      */
8459     /**
8460      * @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)
8461      */
8462     /**
8463      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8464      */
8465      /**
8466      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8467      * @type Boolean
8468      */
8469   
8470
8471     /**
8472      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8473      * @type Boolean
8474      */
8475     /**
8476      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8477      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8478      * a finer-grained basis than the DataProxy events.
8479      */
8480     getConnection : function(){
8481         return this.useAjax ? Roo.Ajax : this.conn;
8482     },
8483
8484     /**
8485      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8486      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8487      * process that block using the passed callback.
8488      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8489      * for the request to the remote server.
8490      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8491      * object into a block of Roo.data.Records.
8492      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8493      * The function must be passed <ul>
8494      * <li>The Record block object</li>
8495      * <li>The "arg" argument from the load function</li>
8496      * <li>A boolean success indicator</li>
8497      * </ul>
8498      * @param {Object} scope The scope in which to call the callback
8499      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8500      */
8501     load : function(params, reader, callback, scope, arg){
8502         if(this.fireEvent("beforeload", this, params) !== false){
8503             var  o = {
8504                 params : params || {},
8505                 request: {
8506                     callback : callback,
8507                     scope : scope,
8508                     arg : arg
8509                 },
8510                 reader: reader,
8511                 callback : this.loadResponse,
8512                 scope: this
8513             };
8514             if(this.useAjax){
8515                 Roo.applyIf(o, this.conn);
8516                 if(this.activeRequest){
8517                     Roo.Ajax.abort(this.activeRequest);
8518                 }
8519                 this.activeRequest = Roo.Ajax.request(o);
8520             }else{
8521                 this.conn.request(o);
8522             }
8523         }else{
8524             callback.call(scope||this, null, arg, false);
8525         }
8526     },
8527
8528     // private
8529     loadResponse : function(o, success, response){
8530         delete this.activeRequest;
8531         if(!success){
8532             this.fireEvent("loadexception", this, o, response);
8533             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8534             return;
8535         }
8536         var result;
8537         try {
8538             result = o.reader.read(response);
8539         }catch(e){
8540             this.fireEvent("loadexception", this, o, response, e);
8541             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8542             return;
8543         }
8544         
8545         this.fireEvent("load", this, o, o.request.arg);
8546         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8547     },
8548
8549     // private
8550     update : function(dataSet){
8551
8552     },
8553
8554     // private
8555     updateResponse : function(dataSet){
8556
8557     }
8558 });/*
8559  * Based on:
8560  * Ext JS Library 1.1.1
8561  * Copyright(c) 2006-2007, Ext JS, LLC.
8562  *
8563  * Originally Released Under LGPL - original licence link has changed is not relivant.
8564  *
8565  * Fork - LGPL
8566  * <script type="text/javascript">
8567  */
8568
8569 /**
8570  * @class Roo.data.ScriptTagProxy
8571  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8572  * other than the originating domain of the running page.<br><br>
8573  * <p>
8574  * <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
8575  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8576  * <p>
8577  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8578  * source code that is used as the source inside a &lt;script> tag.<br><br>
8579  * <p>
8580  * In order for the browser to process the returned data, the server must wrap the data object
8581  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8582  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8583  * depending on whether the callback name was passed:
8584  * <p>
8585  * <pre><code>
8586 boolean scriptTag = false;
8587 String cb = request.getParameter("callback");
8588 if (cb != null) {
8589     scriptTag = true;
8590     response.setContentType("text/javascript");
8591 } else {
8592     response.setContentType("application/x-json");
8593 }
8594 Writer out = response.getWriter();
8595 if (scriptTag) {
8596     out.write(cb + "(");
8597 }
8598 out.print(dataBlock.toJsonString());
8599 if (scriptTag) {
8600     out.write(");");
8601 }
8602 </pre></code>
8603  *
8604  * @constructor
8605  * @param {Object} config A configuration object.
8606  */
8607 Roo.data.ScriptTagProxy = function(config){
8608     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8609     Roo.apply(this, config);
8610     this.head = document.getElementsByTagName("head")[0];
8611 };
8612
8613 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8614
8615 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8616     /**
8617      * @cfg {String} url The URL from which to request the data object.
8618      */
8619     /**
8620      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8621      */
8622     timeout : 30000,
8623     /**
8624      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8625      * the server the name of the callback function set up by the load call to process the returned data object.
8626      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8627      * javascript output which calls this named function passing the data object as its only parameter.
8628      */
8629     callbackParam : "callback",
8630     /**
8631      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8632      * name to the request.
8633      */
8634     nocache : true,
8635
8636     /**
8637      * Load data from the configured URL, read the data object into
8638      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8639      * process that block using the passed callback.
8640      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8641      * for the request to the remote server.
8642      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8643      * object into a block of Roo.data.Records.
8644      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8645      * The function must be passed <ul>
8646      * <li>The Record block object</li>
8647      * <li>The "arg" argument from the load function</li>
8648      * <li>A boolean success indicator</li>
8649      * </ul>
8650      * @param {Object} scope The scope in which to call the callback
8651      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8652      */
8653     load : function(params, reader, callback, scope, arg){
8654         if(this.fireEvent("beforeload", this, params) !== false){
8655
8656             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8657
8658             var url = this.url;
8659             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8660             if(this.nocache){
8661                 url += "&_dc=" + (new Date().getTime());
8662             }
8663             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8664             var trans = {
8665                 id : transId,
8666                 cb : "stcCallback"+transId,
8667                 scriptId : "stcScript"+transId,
8668                 params : params,
8669                 arg : arg,
8670                 url : url,
8671                 callback : callback,
8672                 scope : scope,
8673                 reader : reader
8674             };
8675             var conn = this;
8676
8677             window[trans.cb] = function(o){
8678                 conn.handleResponse(o, trans);
8679             };
8680
8681             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8682
8683             if(this.autoAbort !== false){
8684                 this.abort();
8685             }
8686
8687             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8688
8689             var script = document.createElement("script");
8690             script.setAttribute("src", url);
8691             script.setAttribute("type", "text/javascript");
8692             script.setAttribute("id", trans.scriptId);
8693             this.head.appendChild(script);
8694
8695             this.trans = trans;
8696         }else{
8697             callback.call(scope||this, null, arg, false);
8698         }
8699     },
8700
8701     // private
8702     isLoading : function(){
8703         return this.trans ? true : false;
8704     },
8705
8706     /**
8707      * Abort the current server request.
8708      */
8709     abort : function(){
8710         if(this.isLoading()){
8711             this.destroyTrans(this.trans);
8712         }
8713     },
8714
8715     // private
8716     destroyTrans : function(trans, isLoaded){
8717         this.head.removeChild(document.getElementById(trans.scriptId));
8718         clearTimeout(trans.timeoutId);
8719         if(isLoaded){
8720             window[trans.cb] = undefined;
8721             try{
8722                 delete window[trans.cb];
8723             }catch(e){}
8724         }else{
8725             // if hasn't been loaded, wait for load to remove it to prevent script error
8726             window[trans.cb] = function(){
8727                 window[trans.cb] = undefined;
8728                 try{
8729                     delete window[trans.cb];
8730                 }catch(e){}
8731             };
8732         }
8733     },
8734
8735     // private
8736     handleResponse : function(o, trans){
8737         this.trans = false;
8738         this.destroyTrans(trans, true);
8739         var result;
8740         try {
8741             result = trans.reader.readRecords(o);
8742         }catch(e){
8743             this.fireEvent("loadexception", this, o, trans.arg, e);
8744             trans.callback.call(trans.scope||window, null, trans.arg, false);
8745             return;
8746         }
8747         this.fireEvent("load", this, o, trans.arg);
8748         trans.callback.call(trans.scope||window, result, trans.arg, true);
8749     },
8750
8751     // private
8752     handleFailure : function(trans){
8753         this.trans = false;
8754         this.destroyTrans(trans, false);
8755         this.fireEvent("loadexception", this, null, trans.arg);
8756         trans.callback.call(trans.scope||window, null, trans.arg, false);
8757     }
8758 });/*
8759  * Based on:
8760  * Ext JS Library 1.1.1
8761  * Copyright(c) 2006-2007, Ext JS, LLC.
8762  *
8763  * Originally Released Under LGPL - original licence link has changed is not relivant.
8764  *
8765  * Fork - LGPL
8766  * <script type="text/javascript">
8767  */
8768
8769 /**
8770  * @class Roo.data.JsonReader
8771  * @extends Roo.data.DataReader
8772  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8773  * based on mappings in a provided Roo.data.Record constructor.
8774  * 
8775  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8776  * in the reply previously. 
8777  * 
8778  * <p>
8779  * Example code:
8780  * <pre><code>
8781 var RecordDef = Roo.data.Record.create([
8782     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8783     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8784 ]);
8785 var myReader = new Roo.data.JsonReader({
8786     totalProperty: "results",    // The property which contains the total dataset size (optional)
8787     root: "rows",                // The property which contains an Array of row objects
8788     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8789 }, RecordDef);
8790 </code></pre>
8791  * <p>
8792  * This would consume a JSON file like this:
8793  * <pre><code>
8794 { 'results': 2, 'rows': [
8795     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8796     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8797 }
8798 </code></pre>
8799  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8800  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8801  * paged from the remote server.
8802  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8803  * @cfg {String} root name of the property which contains the Array of row objects.
8804  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8805  * @constructor
8806  * Create a new JsonReader
8807  * @param {Object} meta Metadata configuration options
8808  * @param {Object} recordType Either an Array of field definition objects,
8809  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8810  */
8811 Roo.data.JsonReader = function(meta, recordType){
8812     
8813     meta = meta || {};
8814     // set some defaults:
8815     Roo.applyIf(meta, {
8816         totalProperty: 'total',
8817         successProperty : 'success',
8818         root : 'data',
8819         id : 'id'
8820     });
8821     
8822     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8823 };
8824 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8825     
8826     /**
8827      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8828      * Used by Store query builder to append _requestMeta to params.
8829      * 
8830      */
8831     metaFromRemote : false,
8832     /**
8833      * This method is only used by a DataProxy which has retrieved data from a remote server.
8834      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8835      * @return {Object} data A data block which is used by an Roo.data.Store object as
8836      * a cache of Roo.data.Records.
8837      */
8838     read : function(response){
8839         var json = response.responseText;
8840        
8841         var o = /* eval:var:o */ eval("("+json+")");
8842         if(!o) {
8843             throw {message: "JsonReader.read: Json object not found"};
8844         }
8845         
8846         if(o.metaData){
8847             
8848             delete this.ef;
8849             this.metaFromRemote = true;
8850             this.meta = o.metaData;
8851             this.recordType = Roo.data.Record.create(o.metaData.fields);
8852             this.onMetaChange(this.meta, this.recordType, o);
8853         }
8854         return this.readRecords(o);
8855     },
8856
8857     // private function a store will implement
8858     onMetaChange : function(meta, recordType, o){
8859
8860     },
8861
8862     /**
8863          * @ignore
8864          */
8865     simpleAccess: function(obj, subsc) {
8866         return obj[subsc];
8867     },
8868
8869         /**
8870          * @ignore
8871          */
8872     getJsonAccessor: function(){
8873         var re = /[\[\.]/;
8874         return function(expr) {
8875             try {
8876                 return(re.test(expr))
8877                     ? new Function("obj", "return obj." + expr)
8878                     : function(obj){
8879                         return obj[expr];
8880                     };
8881             } catch(e){}
8882             return Roo.emptyFn;
8883         };
8884     }(),
8885
8886     /**
8887      * Create a data block containing Roo.data.Records from an XML document.
8888      * @param {Object} o An object which contains an Array of row objects in the property specified
8889      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8890      * which contains the total size of the dataset.
8891      * @return {Object} data A data block which is used by an Roo.data.Store object as
8892      * a cache of Roo.data.Records.
8893      */
8894     readRecords : function(o){
8895         /**
8896          * After any data loads, the raw JSON data is available for further custom processing.
8897          * @type Object
8898          */
8899         this.o = o;
8900         var s = this.meta, Record = this.recordType,
8901             f = Record.prototype.fields, fi = f.items, fl = f.length;
8902
8903 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8904         if (!this.ef) {
8905             if(s.totalProperty) {
8906                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8907                 }
8908                 if(s.successProperty) {
8909                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8910                 }
8911                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8912                 if (s.id) {
8913                         var g = this.getJsonAccessor(s.id);
8914                         this.getId = function(rec) {
8915                                 var r = g(rec);
8916                                 return (r === undefined || r === "") ? null : r;
8917                         };
8918                 } else {
8919                         this.getId = function(){return null;};
8920                 }
8921             this.ef = [];
8922             for(var jj = 0; jj < fl; jj++){
8923                 f = fi[jj];
8924                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8925                 this.ef[jj] = this.getJsonAccessor(map);
8926             }
8927         }
8928
8929         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8930         if(s.totalProperty){
8931             var vt = parseInt(this.getTotal(o), 10);
8932             if(!isNaN(vt)){
8933                 totalRecords = vt;
8934             }
8935         }
8936         if(s.successProperty){
8937             var vs = this.getSuccess(o);
8938             if(vs === false || vs === 'false'){
8939                 success = false;
8940             }
8941         }
8942         var records = [];
8943             for(var i = 0; i < c; i++){
8944                     var n = root[i];
8945                 var values = {};
8946                 var id = this.getId(n);
8947                 for(var j = 0; j < fl; j++){
8948                     f = fi[j];
8949                 var v = this.ef[j](n);
8950                 if (!f.convert) {
8951                     Roo.log('missing convert for ' + f.name);
8952                     Roo.log(f);
8953                     continue;
8954                 }
8955                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8956                 }
8957                 var record = new Record(values, id);
8958                 record.json = n;
8959                 records[i] = record;
8960             }
8961             return {
8962             raw : o,
8963                 success : success,
8964                 records : records,
8965                 totalRecords : totalRecords
8966             };
8967     }
8968 });/*
8969  * Based on:
8970  * Ext JS Library 1.1.1
8971  * Copyright(c) 2006-2007, Ext JS, LLC.
8972  *
8973  * Originally Released Under LGPL - original licence link has changed is not relivant.
8974  *
8975  * Fork - LGPL
8976  * <script type="text/javascript">
8977  */
8978
8979 /**
8980  * @class Roo.data.ArrayReader
8981  * @extends Roo.data.DataReader
8982  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8983  * Each element of that Array represents a row of data fields. The
8984  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8985  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8986  * <p>
8987  * Example code:.
8988  * <pre><code>
8989 var RecordDef = Roo.data.Record.create([
8990     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8991     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8992 ]);
8993 var myReader = new Roo.data.ArrayReader({
8994     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8995 }, RecordDef);
8996 </code></pre>
8997  * <p>
8998  * This would consume an Array like this:
8999  * <pre><code>
9000 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9001   </code></pre>
9002  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9003  * @constructor
9004  * Create a new JsonReader
9005  * @param {Object} meta Metadata configuration options.
9006  * @param {Object} recordType Either an Array of field definition objects
9007  * as specified to {@link Roo.data.Record#create},
9008  * or an {@link Roo.data.Record} object
9009  * created using {@link Roo.data.Record#create}.
9010  */
9011 Roo.data.ArrayReader = function(meta, recordType){
9012     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9013 };
9014
9015 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9016     /**
9017      * Create a data block containing Roo.data.Records from an XML document.
9018      * @param {Object} o An Array of row objects which represents the dataset.
9019      * @return {Object} data A data block which is used by an Roo.data.Store object as
9020      * a cache of Roo.data.Records.
9021      */
9022     readRecords : function(o){
9023         var sid = this.meta ? this.meta.id : null;
9024         var recordType = this.recordType, fields = recordType.prototype.fields;
9025         var records = [];
9026         var root = o;
9027             for(var i = 0; i < root.length; i++){
9028                     var n = root[i];
9029                 var values = {};
9030                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9031                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9032                 var f = fields.items[j];
9033                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9034                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9035                 v = f.convert(v);
9036                 values[f.name] = v;
9037             }
9038                 var record = new recordType(values, id);
9039                 record.json = n;
9040                 records[records.length] = record;
9041             }
9042             return {
9043                 records : records,
9044                 totalRecords : records.length
9045             };
9046     }
9047 });/*
9048  * - LGPL
9049  * * 
9050  */
9051
9052 /**
9053  * @class Roo.bootstrap.ComboBox
9054  * @extends Roo.bootstrap.TriggerField
9055  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9056  * @cfg {Boolean} append (true|false) default false
9057  * @constructor
9058  * Create a new ComboBox.
9059  * @param {Object} config Configuration options
9060  */
9061 Roo.bootstrap.ComboBox = function(config){
9062     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9063     this.addEvents({
9064         /**
9065          * @event expand
9066          * Fires when the dropdown list is expanded
9067              * @param {Roo.bootstrap.ComboBox} combo This combo box
9068              */
9069         'expand' : true,
9070         /**
9071          * @event collapse
9072          * Fires when the dropdown list is collapsed
9073              * @param {Roo.bootstrap.ComboBox} combo This combo box
9074              */
9075         'collapse' : true,
9076         /**
9077          * @event beforeselect
9078          * Fires before a list item is selected. Return false to cancel the selection.
9079              * @param {Roo.bootstrap.ComboBox} combo This combo box
9080              * @param {Roo.data.Record} record The data record returned from the underlying store
9081              * @param {Number} index The index of the selected item in the dropdown list
9082              */
9083         'beforeselect' : true,
9084         /**
9085          * @event select
9086          * Fires when a list item is selected
9087              * @param {Roo.bootstrap.ComboBox} combo This combo box
9088              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9089              * @param {Number} index The index of the selected item in the dropdown list
9090              */
9091         'select' : true,
9092         /**
9093          * @event beforequery
9094          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9095          * The event object passed has these properties:
9096              * @param {Roo.bootstrap.ComboBox} combo This combo box
9097              * @param {String} query The query
9098              * @param {Boolean} forceAll true to force "all" query
9099              * @param {Boolean} cancel true to cancel the query
9100              * @param {Object} e The query event object
9101              */
9102         'beforequery': true,
9103          /**
9104          * @event add
9105          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9106              * @param {Roo.bootstrap.ComboBox} combo This combo box
9107              */
9108         'add' : true,
9109         /**
9110          * @event edit
9111          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9112              * @param {Roo.bootstrap.ComboBox} combo This combo box
9113              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9114              */
9115         'edit' : true,
9116         /**
9117          * @event remove
9118          * Fires when the remove value from the combobox array
9119              * @param {Roo.bootstrap.ComboBox} combo This combo box
9120              */
9121         'remove' : true
9122         
9123     });
9124     
9125     
9126     this.selectedIndex = -1;
9127     if(this.mode == 'local'){
9128         if(config.queryDelay === undefined){
9129             this.queryDelay = 10;
9130         }
9131         if(config.minChars === undefined){
9132             this.minChars = 0;
9133         }
9134     }
9135 };
9136
9137 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9138      
9139     /**
9140      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9141      * rendering into an Roo.Editor, defaults to false)
9142      */
9143     /**
9144      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9145      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9146      */
9147     /**
9148      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9149      */
9150     /**
9151      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9152      * the dropdown list (defaults to undefined, with no header element)
9153      */
9154
9155      /**
9156      * @cfg {String/Roo.Template} tpl The template to use to render the output
9157      */
9158      
9159      /**
9160      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9161      */
9162     listWidth: undefined,
9163     /**
9164      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9165      * mode = 'remote' or 'text' if mode = 'local')
9166      */
9167     displayField: undefined,
9168     /**
9169      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9170      * mode = 'remote' or 'value' if mode = 'local'). 
9171      * Note: use of a valueField requires the user make a selection
9172      * in order for a value to be mapped.
9173      */
9174     valueField: undefined,
9175     
9176     
9177     /**
9178      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9179      * field's data value (defaults to the underlying DOM element's name)
9180      */
9181     hiddenName: undefined,
9182     /**
9183      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9184      */
9185     listClass: '',
9186     /**
9187      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9188      */
9189     selectedClass: 'active',
9190     
9191     /**
9192      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9193      */
9194     shadow:'sides',
9195     /**
9196      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9197      * anchor positions (defaults to 'tl-bl')
9198      */
9199     listAlign: 'tl-bl?',
9200     /**
9201      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9202      */
9203     maxHeight: 300,
9204     /**
9205      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9206      * query specified by the allQuery config option (defaults to 'query')
9207      */
9208     triggerAction: 'query',
9209     /**
9210      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9211      * (defaults to 4, does not apply if editable = false)
9212      */
9213     minChars : 4,
9214     /**
9215      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9216      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9217      */
9218     typeAhead: false,
9219     /**
9220      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9221      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9222      */
9223     queryDelay: 500,
9224     /**
9225      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9226      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9227      */
9228     pageSize: 0,
9229     /**
9230      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9231      * when editable = true (defaults to false)
9232      */
9233     selectOnFocus:false,
9234     /**
9235      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9236      */
9237     queryParam: 'query',
9238     /**
9239      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9240      * when mode = 'remote' (defaults to 'Loading...')
9241      */
9242     loadingText: 'Loading...',
9243     /**
9244      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9245      */
9246     resizable: false,
9247     /**
9248      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9249      */
9250     handleHeight : 8,
9251     /**
9252      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9253      * traditional select (defaults to true)
9254      */
9255     editable: true,
9256     /**
9257      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9258      */
9259     allQuery: '',
9260     /**
9261      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9262      */
9263     mode: 'remote',
9264     /**
9265      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9266      * listWidth has a higher value)
9267      */
9268     minListWidth : 70,
9269     /**
9270      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9271      * allow the user to set arbitrary text into the field (defaults to false)
9272      */
9273     forceSelection:false,
9274     /**
9275      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9276      * if typeAhead = true (defaults to 250)
9277      */
9278     typeAheadDelay : 250,
9279     /**
9280      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9281      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9282      */
9283     valueNotFoundText : undefined,
9284     /**
9285      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9286      */
9287     blockFocus : false,
9288     
9289     /**
9290      * @cfg {Boolean} disableClear Disable showing of clear button.
9291      */
9292     disableClear : false,
9293     /**
9294      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9295      */
9296     alwaysQuery : false,
9297     
9298     /**
9299      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9300      */
9301     multiple : false,
9302     
9303     //private
9304     addicon : false,
9305     editicon: false,
9306     
9307     page: 0,
9308     hasQuery: false,
9309     append: false,
9310     loadNext: false,
9311     item: [],
9312     
9313     // element that contains real text value.. (when hidden is used..)
9314      
9315     // private
9316     initEvents: function(){
9317         
9318         if (!this.store) {
9319             throw "can not find store for combo";
9320         }
9321         this.store = Roo.factory(this.store, Roo.data);
9322         
9323         
9324         
9325         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9326         
9327         
9328         if(this.hiddenName){
9329             
9330             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9331             
9332             this.hiddenField.dom.value =
9333                 this.hiddenValue !== undefined ? this.hiddenValue :
9334                 this.value !== undefined ? this.value : '';
9335
9336             // prevent input submission
9337             this.el.dom.removeAttribute('name');
9338             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9339              
9340              
9341         }
9342         //if(Roo.isGecko){
9343         //    this.el.dom.setAttribute('autocomplete', 'off');
9344         //}
9345
9346         var cls = 'x-combo-list';
9347         this.list = this.el.select('ul.dropdown-menu',true).first();
9348
9349         //this.list = new Roo.Layer({
9350         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9351         //});
9352         
9353         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9354         this.list.setWidth(lw);
9355         
9356         this.list.on('mouseover', this.onViewOver, this);
9357         this.list.on('mousemove', this.onViewMove, this);
9358         
9359         this.list.on('scroll', this.onViewScroll, this);
9360         
9361         /*
9362         this.list.swallowEvent('mousewheel');
9363         this.assetHeight = 0;
9364
9365         if(this.title){
9366             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9367             this.assetHeight += this.header.getHeight();
9368         }
9369
9370         this.innerList = this.list.createChild({cls:cls+'-inner'});
9371         this.innerList.on('mouseover', this.onViewOver, this);
9372         this.innerList.on('mousemove', this.onViewMove, this);
9373         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9374         
9375         if(this.allowBlank && !this.pageSize && !this.disableClear){
9376             this.footer = this.list.createChild({cls:cls+'-ft'});
9377             this.pageTb = new Roo.Toolbar(this.footer);
9378            
9379         }
9380         if(this.pageSize){
9381             this.footer = this.list.createChild({cls:cls+'-ft'});
9382             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9383                     {pageSize: this.pageSize});
9384             
9385         }
9386         
9387         if (this.pageTb && this.allowBlank && !this.disableClear) {
9388             var _this = this;
9389             this.pageTb.add(new Roo.Toolbar.Fill(), {
9390                 cls: 'x-btn-icon x-btn-clear',
9391                 text: '&#160;',
9392                 handler: function()
9393                 {
9394                     _this.collapse();
9395                     _this.clearValue();
9396                     _this.onSelect(false, -1);
9397                 }
9398             });
9399         }
9400         if (this.footer) {
9401             this.assetHeight += this.footer.getHeight();
9402         }
9403         */
9404             
9405         if(!this.tpl){
9406             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9407         }
9408
9409         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9410             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9411         });
9412         //this.view.wrapEl.setDisplayed(false);
9413         this.view.on('click', this.onViewClick, this);
9414         
9415         
9416         
9417         this.store.on('beforeload', this.onBeforeLoad, this);
9418         this.store.on('load', this.onLoad, this);
9419         this.store.on('loadexception', this.onLoadException, this);
9420         /*
9421         if(this.resizable){
9422             this.resizer = new Roo.Resizable(this.list,  {
9423                pinned:true, handles:'se'
9424             });
9425             this.resizer.on('resize', function(r, w, h){
9426                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9427                 this.listWidth = w;
9428                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9429                 this.restrictHeight();
9430             }, this);
9431             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9432         }
9433         */
9434         if(!this.editable){
9435             this.editable = true;
9436             this.setEditable(false);
9437         }
9438         
9439         /*
9440         
9441         if (typeof(this.events.add.listeners) != 'undefined') {
9442             
9443             this.addicon = this.wrap.createChild(
9444                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9445        
9446             this.addicon.on('click', function(e) {
9447                 this.fireEvent('add', this);
9448             }, this);
9449         }
9450         if (typeof(this.events.edit.listeners) != 'undefined') {
9451             
9452             this.editicon = this.wrap.createChild(
9453                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9454             if (this.addicon) {
9455                 this.editicon.setStyle('margin-left', '40px');
9456             }
9457             this.editicon.on('click', function(e) {
9458                 
9459                 // we fire even  if inothing is selected..
9460                 this.fireEvent('edit', this, this.lastData );
9461                 
9462             }, this);
9463         }
9464         */
9465         
9466         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9467             "up" : function(e){
9468                 this.inKeyMode = true;
9469                 this.selectPrev();
9470             },
9471
9472             "down" : function(e){
9473                 if(!this.isExpanded()){
9474                     this.onTriggerClick();
9475                 }else{
9476                     this.inKeyMode = true;
9477                     this.selectNext();
9478                 }
9479             },
9480
9481             "enter" : function(e){
9482                 this.onViewClick();
9483                 //return true;
9484             },
9485
9486             "esc" : function(e){
9487                 this.collapse();
9488             },
9489
9490             "tab" : function(e){
9491                 this.collapse();
9492                 
9493                 if(this.fireEvent("specialkey", this, e)){
9494                     this.onViewClick(false);
9495                 }
9496                 
9497                 return true;
9498             },
9499
9500             scope : this,
9501
9502             doRelay : function(foo, bar, hname){
9503                 if(hname == 'down' || this.scope.isExpanded()){
9504                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9505                 }
9506                 return true;
9507             },
9508
9509             forceKeyDown: true
9510         });
9511         
9512         
9513         this.queryDelay = Math.max(this.queryDelay || 10,
9514                 this.mode == 'local' ? 10 : 250);
9515         
9516         
9517         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9518         
9519         if(this.typeAhead){
9520             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9521         }
9522         if(this.editable !== false){
9523             this.inputEl().on("keyup", this.onKeyUp, this);
9524         }
9525         if(this.forceSelection){
9526             this.inputEl().on('blur', this.doForce, this);
9527         }
9528         
9529         if(this.multiple){
9530             this.choices = this.el.select('ul.select2-choices', true).first();
9531             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9532         }
9533     },
9534
9535     onDestroy : function(){
9536         if(this.view){
9537             this.view.setStore(null);
9538             this.view.el.removeAllListeners();
9539             this.view.el.remove();
9540             this.view.purgeListeners();
9541         }
9542         if(this.list){
9543             this.list.dom.innerHTML  = '';
9544         }
9545         if(this.store){
9546             this.store.un('beforeload', this.onBeforeLoad, this);
9547             this.store.un('load', this.onLoad, this);
9548             this.store.un('loadexception', this.onLoadException, this);
9549         }
9550         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9551     },
9552
9553     // private
9554     fireKey : function(e){
9555         if(e.isNavKeyPress() && !this.list.isVisible()){
9556             this.fireEvent("specialkey", this, e);
9557         }
9558     },
9559
9560     // private
9561     onResize: function(w, h){
9562 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9563 //        
9564 //        if(typeof w != 'number'){
9565 //            // we do not handle it!?!?
9566 //            return;
9567 //        }
9568 //        var tw = this.trigger.getWidth();
9569 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9570 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9571 //        var x = w - tw;
9572 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9573 //            
9574 //        //this.trigger.setStyle('left', x+'px');
9575 //        
9576 //        if(this.list && this.listWidth === undefined){
9577 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9578 //            this.list.setWidth(lw);
9579 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9580 //        }
9581         
9582     
9583         
9584     },
9585
9586     /**
9587      * Allow or prevent the user from directly editing the field text.  If false is passed,
9588      * the user will only be able to select from the items defined in the dropdown list.  This method
9589      * is the runtime equivalent of setting the 'editable' config option at config time.
9590      * @param {Boolean} value True to allow the user to directly edit the field text
9591      */
9592     setEditable : function(value){
9593         if(value == this.editable){
9594             return;
9595         }
9596         this.editable = value;
9597         if(!value){
9598             this.inputEl().dom.setAttribute('readOnly', true);
9599             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9600             this.inputEl().addClass('x-combo-noedit');
9601         }else{
9602             this.inputEl().dom.setAttribute('readOnly', false);
9603             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9604             this.inputEl().removeClass('x-combo-noedit');
9605         }
9606     },
9607
9608     // private
9609     
9610     onBeforeLoad : function(combo,opts){
9611         if(!this.hasFocus){
9612             return;
9613         }
9614          if (!opts.add) {
9615             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9616          }
9617         this.restrictHeight();
9618         this.selectedIndex = -1;
9619     },
9620
9621     // private
9622     onLoad : function(){
9623         
9624         this.hasQuery = false;
9625         
9626         if(!this.hasFocus){
9627             return;
9628         }
9629         
9630         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9631             this.loading.hide();
9632         }
9633         
9634         if(this.store.getCount() > 0){
9635             this.expand();
9636             this.restrictHeight();
9637             if(this.lastQuery == this.allQuery){
9638                 if(this.editable){
9639                     this.inputEl().dom.select();
9640                 }
9641                 if(!this.selectByValue(this.value, true)){
9642                     this.select(0, true);
9643                 }
9644             }else{
9645                 this.selectNext();
9646                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9647                     this.taTask.delay(this.typeAheadDelay);
9648                 }
9649             }
9650         }else{
9651             this.onEmptyResults();
9652         }
9653         
9654         //this.el.focus();
9655     },
9656     // private
9657     onLoadException : function()
9658     {
9659         this.hasQuery = false;
9660         
9661         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9662             this.loading.hide();
9663         }
9664         
9665         this.collapse();
9666         Roo.log(this.store.reader.jsonData);
9667         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9668             // fixme
9669             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9670         }
9671         
9672         
9673     },
9674     // private
9675     onTypeAhead : function(){
9676         if(this.store.getCount() > 0){
9677             var r = this.store.getAt(0);
9678             var newValue = r.data[this.displayField];
9679             var len = newValue.length;
9680             var selStart = this.getRawValue().length;
9681             
9682             if(selStart != len){
9683                 this.setRawValue(newValue);
9684                 this.selectText(selStart, newValue.length);
9685             }
9686         }
9687     },
9688
9689     // private
9690     onSelect : function(record, index){
9691         
9692         if(this.fireEvent('beforeselect', this, record, index) !== false){
9693         
9694             this.setFromData(index > -1 ? record.data : false);
9695             
9696             this.collapse();
9697             this.fireEvent('select', this, record, index);
9698         }
9699     },
9700
9701     /**
9702      * Returns the currently selected field value or empty string if no value is set.
9703      * @return {String} value The selected value
9704      */
9705     getValue : function(){
9706         
9707         if(this.multiple){
9708             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9709         }
9710         
9711         if(this.valueField){
9712             return typeof this.value != 'undefined' ? this.value : '';
9713         }else{
9714             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9715         }
9716     },
9717
9718     /**
9719      * Clears any text/value currently set in the field
9720      */
9721     clearValue : function(){
9722         if(this.hiddenField){
9723             this.hiddenField.dom.value = '';
9724         }
9725         this.value = '';
9726         this.setRawValue('');
9727         this.lastSelectionText = '';
9728         
9729     },
9730
9731     /**
9732      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9733      * will be displayed in the field.  If the value does not match the data value of an existing item,
9734      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9735      * Otherwise the field will be blank (although the value will still be set).
9736      * @param {String} value The value to match
9737      */
9738     setValue : function(v){
9739         if(this.multiple){
9740             this.syncValue();
9741             return;
9742         }
9743         
9744         var text = v;
9745         if(this.valueField){
9746             var r = this.findRecord(this.valueField, v);
9747             if(r){
9748                 text = r.data[this.displayField];
9749             }else if(this.valueNotFoundText !== undefined){
9750                 text = this.valueNotFoundText;
9751             }
9752         }
9753         this.lastSelectionText = text;
9754         if(this.hiddenField){
9755             this.hiddenField.dom.value = v;
9756         }
9757         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9758         this.value = v;
9759     },
9760     /**
9761      * @property {Object} the last set data for the element
9762      */
9763     
9764     lastData : false,
9765     /**
9766      * Sets the value of the field based on a object which is related to the record format for the store.
9767      * @param {Object} value the value to set as. or false on reset?
9768      */
9769     setFromData : function(o){
9770         
9771         if(this.multiple){
9772             this.addItem(o);
9773             return;
9774         }
9775             
9776         var dv = ''; // display value
9777         var vv = ''; // value value..
9778         this.lastData = o;
9779         if (this.displayField) {
9780             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9781         } else {
9782             // this is an error condition!!!
9783             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9784         }
9785         
9786         if(this.valueField){
9787             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9788         }
9789         
9790         if(this.hiddenField){
9791             this.hiddenField.dom.value = vv;
9792             
9793             this.lastSelectionText = dv;
9794             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9795             this.value = vv;
9796             return;
9797         }
9798         // no hidden field.. - we store the value in 'value', but still display
9799         // display field!!!!
9800         this.lastSelectionText = dv;
9801         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9802         this.value = vv;
9803         
9804         
9805     },
9806     // private
9807     reset : function(){
9808         // overridden so that last data is reset..
9809         this.setValue(this.originalValue);
9810         this.clearInvalid();
9811         this.lastData = false;
9812         if (this.view) {
9813             this.view.clearSelections();
9814         }
9815     },
9816     // private
9817     findRecord : function(prop, value){
9818         var record;
9819         if(this.store.getCount() > 0){
9820             this.store.each(function(r){
9821                 if(r.data[prop] == value){
9822                     record = r;
9823                     return false;
9824                 }
9825                 return true;
9826             });
9827         }
9828         return record;
9829     },
9830     
9831     getName: function()
9832     {
9833         // returns hidden if it's set..
9834         if (!this.rendered) {return ''};
9835         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9836         
9837     },
9838     // private
9839     onViewMove : function(e, t){
9840         this.inKeyMode = false;
9841     },
9842
9843     // private
9844     onViewOver : function(e, t){
9845         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9846             return;
9847         }
9848         var item = this.view.findItemFromChild(t);
9849         if(item){
9850             var index = this.view.indexOf(item);
9851             this.select(index, false);
9852         }
9853     },
9854
9855     // private
9856     onViewClick : function(doFocus)
9857     {
9858         var index = this.view.getSelectedIndexes()[0];
9859         var r = this.store.getAt(index);
9860         if(r){
9861             this.onSelect(r, index);
9862         }
9863         if(doFocus !== false && !this.blockFocus){
9864             this.inputEl().focus();
9865         }
9866     },
9867
9868     // private
9869     restrictHeight : function(){
9870         //this.innerList.dom.style.height = '';
9871         //var inner = this.innerList.dom;
9872         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9873         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9874         //this.list.beginUpdate();
9875         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9876         this.list.alignTo(this.inputEl(), this.listAlign);
9877         //this.list.endUpdate();
9878     },
9879
9880     // private
9881     onEmptyResults : function(){
9882         this.collapse();
9883     },
9884
9885     /**
9886      * Returns true if the dropdown list is expanded, else false.
9887      */
9888     isExpanded : function(){
9889         return this.list.isVisible();
9890     },
9891
9892     /**
9893      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9894      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9895      * @param {String} value The data value of the item to select
9896      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9897      * selected item if it is not currently in view (defaults to true)
9898      * @return {Boolean} True if the value matched an item in the list, else false
9899      */
9900     selectByValue : function(v, scrollIntoView){
9901         if(v !== undefined && v !== null){
9902             var r = this.findRecord(this.valueField || this.displayField, v);
9903             if(r){
9904                 this.select(this.store.indexOf(r), scrollIntoView);
9905                 return true;
9906             }
9907         }
9908         return false;
9909     },
9910
9911     /**
9912      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9913      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9914      * @param {Number} index The zero-based index of the list item to select
9915      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9916      * selected item if it is not currently in view (defaults to true)
9917      */
9918     select : function(index, scrollIntoView){
9919         this.selectedIndex = index;
9920         this.view.select(index);
9921         if(scrollIntoView !== false){
9922             var el = this.view.getNode(index);
9923             if(el){
9924                 //this.innerList.scrollChildIntoView(el, false);
9925                 
9926             }
9927         }
9928     },
9929
9930     // private
9931     selectNext : function(){
9932         var ct = this.store.getCount();
9933         if(ct > 0){
9934             if(this.selectedIndex == -1){
9935                 this.select(0);
9936             }else if(this.selectedIndex < ct-1){
9937                 this.select(this.selectedIndex+1);
9938             }
9939         }
9940     },
9941
9942     // private
9943     selectPrev : function(){
9944         var ct = this.store.getCount();
9945         if(ct > 0){
9946             if(this.selectedIndex == -1){
9947                 this.select(0);
9948             }else if(this.selectedIndex != 0){
9949                 this.select(this.selectedIndex-1);
9950             }
9951         }
9952     },
9953
9954     // private
9955     onKeyUp : function(e){
9956         if(this.editable !== false && !e.isSpecialKey()){
9957             this.lastKey = e.getKey();
9958             this.dqTask.delay(this.queryDelay);
9959         }
9960     },
9961
9962     // private
9963     validateBlur : function(){
9964         return !this.list || !this.list.isVisible();   
9965     },
9966
9967     // private
9968     initQuery : function(){
9969         this.doQuery(this.getRawValue());
9970     },
9971
9972     // private
9973     doForce : function(){
9974         if(this.inputEl().dom.value.length > 0){
9975             this.inputEl().dom.value =
9976                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9977              
9978         }
9979     },
9980
9981     /**
9982      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9983      * query allowing the query action to be canceled if needed.
9984      * @param {String} query The SQL query to execute
9985      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9986      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9987      * saved in the current store (defaults to false)
9988      */
9989     doQuery : function(q, forceAll){
9990         
9991         if(q === undefined || q === null){
9992             q = '';
9993         }
9994         var qe = {
9995             query: q,
9996             forceAll: forceAll,
9997             combo: this,
9998             cancel:false
9999         };
10000         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10001             return false;
10002         }
10003         q = qe.query;
10004         
10005         forceAll = qe.forceAll;
10006         if(forceAll === true || (q.length >= this.minChars)){
10007             
10008             this.hasQuery = true;
10009             
10010             if(this.lastQuery != q || this.alwaysQuery){
10011                 this.lastQuery = q;
10012                 if(this.mode == 'local'){
10013                     this.selectedIndex = -1;
10014                     if(forceAll){
10015                         this.store.clearFilter();
10016                     }else{
10017                         this.store.filter(this.displayField, q);
10018                     }
10019                     this.onLoad();
10020                 }else{
10021                     this.store.baseParams[this.queryParam] = q;
10022                     
10023                     var options = {params : this.getParams(q)};
10024                     
10025                     if(this.loadNext){
10026                         options.add = true;
10027                         options.params.start = this.page * this.pageSize;
10028                     }
10029                     
10030                     this.store.load(options);
10031                     this.expand();
10032                 }
10033             }else{
10034                 this.selectedIndex = -1;
10035                 this.onLoad();   
10036             }
10037         }
10038         
10039         this.loadNext = false;
10040     },
10041
10042     // private
10043     getParams : function(q){
10044         var p = {};
10045         //p[this.queryParam] = q;
10046         
10047         if(this.pageSize){
10048             p.start = 0;
10049             p.limit = this.pageSize;
10050         }
10051         return p;
10052     },
10053
10054     /**
10055      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10056      */
10057     collapse : function(){
10058         if(!this.isExpanded()){
10059             return;
10060         }
10061         
10062         this.list.hide();
10063         Roo.get(document).un('mousedown', this.collapseIf, this);
10064         Roo.get(document).un('mousewheel', this.collapseIf, this);
10065         if (!this.editable) {
10066             Roo.get(document).un('keydown', this.listKeyPress, this);
10067         }
10068         this.fireEvent('collapse', this);
10069     },
10070
10071     // private
10072     collapseIf : function(e){
10073         var in_combo  = e.within(this.el);
10074         var in_list =  e.within(this.list);
10075         
10076         if (in_combo || in_list) {
10077             //e.stopPropagation();
10078             return;
10079         }
10080
10081         this.collapse();
10082         
10083     },
10084
10085     /**
10086      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10087      */
10088     expand : function(){
10089        
10090         if(this.isExpanded() || !this.hasFocus){
10091             return;
10092         }
10093          Roo.log('expand');
10094         this.list.alignTo(this.inputEl(), this.listAlign);
10095         this.list.show();
10096         Roo.get(document).on('mousedown', this.collapseIf, this);
10097         Roo.get(document).on('mousewheel', this.collapseIf, this);
10098         if (!this.editable) {
10099             Roo.get(document).on('keydown', this.listKeyPress, this);
10100         }
10101         
10102         this.fireEvent('expand', this);
10103     },
10104
10105     // private
10106     // Implements the default empty TriggerField.onTriggerClick function
10107     onTriggerClick : function()
10108     {
10109         Roo.log('trigger click');
10110         
10111         if(this.disabled){
10112             return;
10113         }
10114         
10115         this.page = 0;
10116         this.loadNext = false;
10117         
10118         if(this.isExpanded()){
10119             this.collapse();
10120             if (!this.blockFocus) {
10121                 this.inputEl().focus();
10122             }
10123             
10124         }else {
10125             this.hasFocus = true;
10126             if(this.triggerAction == 'all') {
10127                 this.doQuery(this.allQuery, true);
10128             } else {
10129                 this.doQuery(this.getRawValue());
10130             }
10131             if (!this.blockFocus) {
10132                 this.inputEl().focus();
10133             }
10134         }
10135     },
10136     listKeyPress : function(e)
10137     {
10138         //Roo.log('listkeypress');
10139         // scroll to first matching element based on key pres..
10140         if (e.isSpecialKey()) {
10141             return false;
10142         }
10143         var k = String.fromCharCode(e.getKey()).toUpperCase();
10144         //Roo.log(k);
10145         var match  = false;
10146         var csel = this.view.getSelectedNodes();
10147         var cselitem = false;
10148         if (csel.length) {
10149             var ix = this.view.indexOf(csel[0]);
10150             cselitem  = this.store.getAt(ix);
10151             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10152                 cselitem = false;
10153             }
10154             
10155         }
10156         
10157         this.store.each(function(v) { 
10158             if (cselitem) {
10159                 // start at existing selection.
10160                 if (cselitem.id == v.id) {
10161                     cselitem = false;
10162                 }
10163                 return true;
10164             }
10165                 
10166             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10167                 match = this.store.indexOf(v);
10168                 return false;
10169             }
10170             return true;
10171         }, this);
10172         
10173         if (match === false) {
10174             return true; // no more action?
10175         }
10176         // scroll to?
10177         this.view.select(match);
10178         var sn = Roo.get(this.view.getSelectedNodes()[0])
10179         //sn.scrollIntoView(sn.dom.parentNode, false);
10180     },
10181     
10182     onViewScroll : function(e, t){
10183         
10184         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10185             return;
10186         }
10187         
10188         this.hasQuery = true;
10189         
10190         this.loading = this.list.select('.loading', true).first();
10191         
10192         if(this.loading === null){
10193             this.list.createChild({
10194                 tag: 'div',
10195                 cls: 'loading select2-more-results select2-active',
10196                 html: 'Loading more results...'
10197             })
10198             
10199             this.loading = this.list.select('.loading', true).first();
10200             
10201             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10202             
10203             this.loading.hide();
10204         }
10205         
10206         this.loading.show();
10207         
10208         var _combo = this;
10209         
10210         this.page++;
10211         this.loadNext = true;
10212         
10213         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10214         
10215         return;
10216     },
10217     
10218     addItem : function(o)
10219     {   
10220         var dv = ''; // display value
10221         
10222         if (this.displayField) {
10223             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10224         } else {
10225             // this is an error condition!!!
10226             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10227         }
10228         
10229         if(!dv.length){
10230             return;
10231         }
10232         
10233         var choice = this.choices.createChild({
10234             tag: 'li',
10235             cls: 'select2-search-choice',
10236             cn: [
10237                 {
10238                     tag: 'div',
10239                     html: dv
10240                 },
10241                 {
10242                     tag: 'a',
10243                     href: '#',
10244                     cls: 'select2-search-choice-close',
10245                     tabindex: '-1'
10246                 }
10247             ]
10248             
10249         }, this.searchField);
10250         
10251         var close = choice.select('a.select2-search-choice-close', true).first()
10252         
10253         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10254         
10255         this.item.push(o);
10256         this.lastData = o;
10257         
10258         this.syncValue();
10259         
10260         this.inputEl().dom.value = '';
10261         
10262     },
10263     
10264     onRemoveItem : function(e, _self, o)
10265     {
10266         Roo.log('remove item');
10267         var index = this.item.indexOf(o.data) * 1;
10268         
10269         if( index < 0){
10270             Roo.log('not this item?!');
10271             return;
10272         }
10273         
10274         this.item.splice(index, 1);
10275         o.item.remove();
10276         
10277         this.syncValue();
10278         
10279         this.fireEvent('remove', this, e);
10280         
10281     },
10282     
10283     syncValue : function()
10284     {
10285         if(!this.item.length){
10286             this.clearValue();
10287             return;
10288         }
10289             
10290         var value = [];
10291         var _this = this;
10292         Roo.each(this.item, function(i){
10293             if(_this.valueField){
10294                 value.push(i[_this.valueField]);
10295                 return;
10296             }
10297
10298             value.push(i);
10299         });
10300
10301         this.value = value.join(',');
10302
10303         if(this.hiddenField){
10304             this.hiddenField.dom.value = this.value;
10305         }
10306     },
10307     
10308     clearItem : function()
10309     {
10310         if(!this.multiple){
10311             return;
10312         }
10313         
10314         this.item = [];
10315         
10316         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10317            c.remove();
10318         });
10319         
10320         this.syncValue();
10321     }
10322     
10323     
10324
10325     /** 
10326     * @cfg {Boolean} grow 
10327     * @hide 
10328     */
10329     /** 
10330     * @cfg {Number} growMin 
10331     * @hide 
10332     */
10333     /** 
10334     * @cfg {Number} growMax 
10335     * @hide 
10336     */
10337     /**
10338      * @hide
10339      * @method autoSize
10340      */
10341 });
10342 /*
10343  * Based on:
10344  * Ext JS Library 1.1.1
10345  * Copyright(c) 2006-2007, Ext JS, LLC.
10346  *
10347  * Originally Released Under LGPL - original licence link has changed is not relivant.
10348  *
10349  * Fork - LGPL
10350  * <script type="text/javascript">
10351  */
10352
10353 /**
10354  * @class Roo.View
10355  * @extends Roo.util.Observable
10356  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10357  * This class also supports single and multi selection modes. <br>
10358  * Create a data model bound view:
10359  <pre><code>
10360  var store = new Roo.data.Store(...);
10361
10362  var view = new Roo.View({
10363     el : "my-element",
10364     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10365  
10366     singleSelect: true,
10367     selectedClass: "ydataview-selected",
10368     store: store
10369  });
10370
10371  // listen for node click?
10372  view.on("click", function(vw, index, node, e){
10373  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10374  });
10375
10376  // load XML data
10377  dataModel.load("foobar.xml");
10378  </code></pre>
10379  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10380  * <br><br>
10381  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10382  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10383  * 
10384  * Note: old style constructor is still suported (container, template, config)
10385  * 
10386  * @constructor
10387  * Create a new View
10388  * @param {Object} config The config object
10389  * 
10390  */
10391 Roo.View = function(config, depreciated_tpl, depreciated_config){
10392     
10393     if (typeof(depreciated_tpl) == 'undefined') {
10394         // new way.. - universal constructor.
10395         Roo.apply(this, config);
10396         this.el  = Roo.get(this.el);
10397     } else {
10398         // old format..
10399         this.el  = Roo.get(config);
10400         this.tpl = depreciated_tpl;
10401         Roo.apply(this, depreciated_config);
10402     }
10403     this.wrapEl  = this.el.wrap().wrap();
10404     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10405     
10406     
10407     if(typeof(this.tpl) == "string"){
10408         this.tpl = new Roo.Template(this.tpl);
10409     } else {
10410         // support xtype ctors..
10411         this.tpl = new Roo.factory(this.tpl, Roo);
10412     }
10413     
10414     
10415     this.tpl.compile();
10416    
10417   
10418     
10419      
10420     /** @private */
10421     this.addEvents({
10422         /**
10423          * @event beforeclick
10424          * Fires before a click is processed. Returns false to cancel the default action.
10425          * @param {Roo.View} this
10426          * @param {Number} index The index of the target node
10427          * @param {HTMLElement} node The target node
10428          * @param {Roo.EventObject} e The raw event object
10429          */
10430             "beforeclick" : true,
10431         /**
10432          * @event click
10433          * Fires when a template node is clicked.
10434          * @param {Roo.View} this
10435          * @param {Number} index The index of the target node
10436          * @param {HTMLElement} node The target node
10437          * @param {Roo.EventObject} e The raw event object
10438          */
10439             "click" : true,
10440         /**
10441          * @event dblclick
10442          * Fires when a template node is double clicked.
10443          * @param {Roo.View} this
10444          * @param {Number} index The index of the target node
10445          * @param {HTMLElement} node The target node
10446          * @param {Roo.EventObject} e The raw event object
10447          */
10448             "dblclick" : true,
10449         /**
10450          * @event contextmenu
10451          * Fires when a template node is right clicked.
10452          * @param {Roo.View} this
10453          * @param {Number} index The index of the target node
10454          * @param {HTMLElement} node The target node
10455          * @param {Roo.EventObject} e The raw event object
10456          */
10457             "contextmenu" : true,
10458         /**
10459          * @event selectionchange
10460          * Fires when the selected nodes change.
10461          * @param {Roo.View} this
10462          * @param {Array} selections Array of the selected nodes
10463          */
10464             "selectionchange" : true,
10465     
10466         /**
10467          * @event beforeselect
10468          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10469          * @param {Roo.View} this
10470          * @param {HTMLElement} node The node to be selected
10471          * @param {Array} selections Array of currently selected nodes
10472          */
10473             "beforeselect" : true,
10474         /**
10475          * @event preparedata
10476          * Fires on every row to render, to allow you to change the data.
10477          * @param {Roo.View} this
10478          * @param {Object} data to be rendered (change this)
10479          */
10480           "preparedata" : true
10481           
10482           
10483         });
10484
10485
10486
10487     this.el.on({
10488         "click": this.onClick,
10489         "dblclick": this.onDblClick,
10490         "contextmenu": this.onContextMenu,
10491         scope:this
10492     });
10493
10494     this.selections = [];
10495     this.nodes = [];
10496     this.cmp = new Roo.CompositeElementLite([]);
10497     if(this.store){
10498         this.store = Roo.factory(this.store, Roo.data);
10499         this.setStore(this.store, true);
10500     }
10501     
10502     if ( this.footer && this.footer.xtype) {
10503            
10504          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10505         
10506         this.footer.dataSource = this.store
10507         this.footer.container = fctr;
10508         this.footer = Roo.factory(this.footer, Roo);
10509         fctr.insertFirst(this.el);
10510         
10511         // this is a bit insane - as the paging toolbar seems to detach the el..
10512 //        dom.parentNode.parentNode.parentNode
10513          // they get detached?
10514     }
10515     
10516     
10517     Roo.View.superclass.constructor.call(this);
10518     
10519     
10520 };
10521
10522 Roo.extend(Roo.View, Roo.util.Observable, {
10523     
10524      /**
10525      * @cfg {Roo.data.Store} store Data store to load data from.
10526      */
10527     store : false,
10528     
10529     /**
10530      * @cfg {String|Roo.Element} el The container element.
10531      */
10532     el : '',
10533     
10534     /**
10535      * @cfg {String|Roo.Template} tpl The template used by this View 
10536      */
10537     tpl : false,
10538     /**
10539      * @cfg {String} dataName the named area of the template to use as the data area
10540      *                          Works with domtemplates roo-name="name"
10541      */
10542     dataName: false,
10543     /**
10544      * @cfg {String} selectedClass The css class to add to selected nodes
10545      */
10546     selectedClass : "x-view-selected",
10547      /**
10548      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10549      */
10550     emptyText : "",
10551     
10552     /**
10553      * @cfg {String} text to display on mask (default Loading)
10554      */
10555     mask : false,
10556     /**
10557      * @cfg {Boolean} multiSelect Allow multiple selection
10558      */
10559     multiSelect : false,
10560     /**
10561      * @cfg {Boolean} singleSelect Allow single selection
10562      */
10563     singleSelect:  false,
10564     
10565     /**
10566      * @cfg {Boolean} toggleSelect - selecting 
10567      */
10568     toggleSelect : false,
10569     
10570     /**
10571      * Returns the element this view is bound to.
10572      * @return {Roo.Element}
10573      */
10574     getEl : function(){
10575         return this.wrapEl;
10576     },
10577     
10578     
10579
10580     /**
10581      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10582      */
10583     refresh : function(){
10584         Roo.log('refresh');
10585         var t = this.tpl;
10586         
10587         // if we are using something like 'domtemplate', then
10588         // the what gets used is:
10589         // t.applySubtemplate(NAME, data, wrapping data..)
10590         // the outer template then get' applied with
10591         //     the store 'extra data'
10592         // and the body get's added to the
10593         //      roo-name="data" node?
10594         //      <span class='roo-tpl-{name}'></span> ?????
10595         
10596         
10597         
10598         this.clearSelections();
10599         this.el.update("");
10600         var html = [];
10601         var records = this.store.getRange();
10602         if(records.length < 1) {
10603             
10604             // is this valid??  = should it render a template??
10605             
10606             this.el.update(this.emptyText);
10607             return;
10608         }
10609         var el = this.el;
10610         if (this.dataName) {
10611             this.el.update(t.apply(this.store.meta)); //????
10612             el = this.el.child('.roo-tpl-' + this.dataName);
10613         }
10614         
10615         for(var i = 0, len = records.length; i < len; i++){
10616             var data = this.prepareData(records[i].data, i, records[i]);
10617             this.fireEvent("preparedata", this, data, i, records[i]);
10618             html[html.length] = Roo.util.Format.trim(
10619                 this.dataName ?
10620                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10621                     t.apply(data)
10622             );
10623         }
10624         
10625         
10626         
10627         el.update(html.join(""));
10628         this.nodes = el.dom.childNodes;
10629         this.updateIndexes(0);
10630     },
10631     
10632
10633     /**
10634      * Function to override to reformat the data that is sent to
10635      * the template for each node.
10636      * DEPRICATED - use the preparedata event handler.
10637      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10638      * a JSON object for an UpdateManager bound view).
10639      */
10640     prepareData : function(data, index, record)
10641     {
10642         this.fireEvent("preparedata", this, data, index, record);
10643         return data;
10644     },
10645
10646     onUpdate : function(ds, record){
10647          Roo.log('on update');   
10648         this.clearSelections();
10649         var index = this.store.indexOf(record);
10650         var n = this.nodes[index];
10651         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10652         n.parentNode.removeChild(n);
10653         this.updateIndexes(index, index);
10654     },
10655
10656     
10657     
10658 // --------- FIXME     
10659     onAdd : function(ds, records, index)
10660     {
10661         Roo.log(['on Add', ds, records, index] );        
10662         this.clearSelections();
10663         if(this.nodes.length == 0){
10664             this.refresh();
10665             return;
10666         }
10667         var n = this.nodes[index];
10668         for(var i = 0, len = records.length; i < len; i++){
10669             var d = this.prepareData(records[i].data, i, records[i]);
10670             if(n){
10671                 this.tpl.insertBefore(n, d);
10672             }else{
10673                 
10674                 this.tpl.append(this.el, d);
10675             }
10676         }
10677         this.updateIndexes(index);
10678     },
10679
10680     onRemove : function(ds, record, index){
10681         Roo.log('onRemove');
10682         this.clearSelections();
10683         var el = this.dataName  ?
10684             this.el.child('.roo-tpl-' + this.dataName) :
10685             this.el; 
10686         
10687         el.dom.removeChild(this.nodes[index]);
10688         this.updateIndexes(index);
10689     },
10690
10691     /**
10692      * Refresh an individual node.
10693      * @param {Number} index
10694      */
10695     refreshNode : function(index){
10696         this.onUpdate(this.store, this.store.getAt(index));
10697     },
10698
10699     updateIndexes : function(startIndex, endIndex){
10700         var ns = this.nodes;
10701         startIndex = startIndex || 0;
10702         endIndex = endIndex || ns.length - 1;
10703         for(var i = startIndex; i <= endIndex; i++){
10704             ns[i].nodeIndex = i;
10705         }
10706     },
10707
10708     /**
10709      * Changes the data store this view uses and refresh the view.
10710      * @param {Store} store
10711      */
10712     setStore : function(store, initial){
10713         if(!initial && this.store){
10714             this.store.un("datachanged", this.refresh);
10715             this.store.un("add", this.onAdd);
10716             this.store.un("remove", this.onRemove);
10717             this.store.un("update", this.onUpdate);
10718             this.store.un("clear", this.refresh);
10719             this.store.un("beforeload", this.onBeforeLoad);
10720             this.store.un("load", this.onLoad);
10721             this.store.un("loadexception", this.onLoad);
10722         }
10723         if(store){
10724           
10725             store.on("datachanged", this.refresh, this);
10726             store.on("add", this.onAdd, this);
10727             store.on("remove", this.onRemove, this);
10728             store.on("update", this.onUpdate, this);
10729             store.on("clear", this.refresh, this);
10730             store.on("beforeload", this.onBeforeLoad, this);
10731             store.on("load", this.onLoad, this);
10732             store.on("loadexception", this.onLoad, this);
10733         }
10734         
10735         if(store){
10736             this.refresh();
10737         }
10738     },
10739     /**
10740      * onbeforeLoad - masks the loading area.
10741      *
10742      */
10743     onBeforeLoad : function(store,opts)
10744     {
10745          Roo.log('onBeforeLoad');   
10746         if (!opts.add) {
10747             this.el.update("");
10748         }
10749         this.el.mask(this.mask ? this.mask : "Loading" ); 
10750     },
10751     onLoad : function ()
10752     {
10753         this.el.unmask();
10754     },
10755     
10756
10757     /**
10758      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10759      * @param {HTMLElement} node
10760      * @return {HTMLElement} The template node
10761      */
10762     findItemFromChild : function(node){
10763         var el = this.dataName  ?
10764             this.el.child('.roo-tpl-' + this.dataName,true) :
10765             this.el.dom; 
10766         
10767         if(!node || node.parentNode == el){
10768                     return node;
10769             }
10770             var p = node.parentNode;
10771             while(p && p != el){
10772             if(p.parentNode == el){
10773                 return p;
10774             }
10775             p = p.parentNode;
10776         }
10777             return null;
10778     },
10779
10780     /** @ignore */
10781     onClick : function(e){
10782         var item = this.findItemFromChild(e.getTarget());
10783         if(item){
10784             var index = this.indexOf(item);
10785             if(this.onItemClick(item, index, e) !== false){
10786                 this.fireEvent("click", this, index, item, e);
10787             }
10788         }else{
10789             this.clearSelections();
10790         }
10791     },
10792
10793     /** @ignore */
10794     onContextMenu : function(e){
10795         var item = this.findItemFromChild(e.getTarget());
10796         if(item){
10797             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10798         }
10799     },
10800
10801     /** @ignore */
10802     onDblClick : function(e){
10803         var item = this.findItemFromChild(e.getTarget());
10804         if(item){
10805             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10806         }
10807     },
10808
10809     onItemClick : function(item, index, e)
10810     {
10811         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10812             return false;
10813         }
10814         if (this.toggleSelect) {
10815             var m = this.isSelected(item) ? 'unselect' : 'select';
10816             Roo.log(m);
10817             var _t = this;
10818             _t[m](item, true, false);
10819             return true;
10820         }
10821         if(this.multiSelect || this.singleSelect){
10822             if(this.multiSelect && e.shiftKey && this.lastSelection){
10823                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10824             }else{
10825                 this.select(item, this.multiSelect && e.ctrlKey);
10826                 this.lastSelection = item;
10827             }
10828             e.preventDefault();
10829         }
10830         return true;
10831     },
10832
10833     /**
10834      * Get the number of selected nodes.
10835      * @return {Number}
10836      */
10837     getSelectionCount : function(){
10838         return this.selections.length;
10839     },
10840
10841     /**
10842      * Get the currently selected nodes.
10843      * @return {Array} An array of HTMLElements
10844      */
10845     getSelectedNodes : function(){
10846         return this.selections;
10847     },
10848
10849     /**
10850      * Get the indexes of the selected nodes.
10851      * @return {Array}
10852      */
10853     getSelectedIndexes : function(){
10854         var indexes = [], s = this.selections;
10855         for(var i = 0, len = s.length; i < len; i++){
10856             indexes.push(s[i].nodeIndex);
10857         }
10858         return indexes;
10859     },
10860
10861     /**
10862      * Clear all selections
10863      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10864      */
10865     clearSelections : function(suppressEvent){
10866         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10867             this.cmp.elements = this.selections;
10868             this.cmp.removeClass(this.selectedClass);
10869             this.selections = [];
10870             if(!suppressEvent){
10871                 this.fireEvent("selectionchange", this, this.selections);
10872             }
10873         }
10874     },
10875
10876     /**
10877      * Returns true if the passed node is selected
10878      * @param {HTMLElement/Number} node The node or node index
10879      * @return {Boolean}
10880      */
10881     isSelected : function(node){
10882         var s = this.selections;
10883         if(s.length < 1){
10884             return false;
10885         }
10886         node = this.getNode(node);
10887         return s.indexOf(node) !== -1;
10888     },
10889
10890     /**
10891      * Selects nodes.
10892      * @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
10893      * @param {Boolean} keepExisting (optional) true to keep existing selections
10894      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10895      */
10896     select : function(nodeInfo, keepExisting, suppressEvent){
10897         if(nodeInfo instanceof Array){
10898             if(!keepExisting){
10899                 this.clearSelections(true);
10900             }
10901             for(var i = 0, len = nodeInfo.length; i < len; i++){
10902                 this.select(nodeInfo[i], true, true);
10903             }
10904             return;
10905         } 
10906         var node = this.getNode(nodeInfo);
10907         if(!node || this.isSelected(node)){
10908             return; // already selected.
10909         }
10910         if(!keepExisting){
10911             this.clearSelections(true);
10912         }
10913         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10914             Roo.fly(node).addClass(this.selectedClass);
10915             this.selections.push(node);
10916             if(!suppressEvent){
10917                 this.fireEvent("selectionchange", this, this.selections);
10918             }
10919         }
10920         
10921         
10922     },
10923       /**
10924      * Unselects nodes.
10925      * @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
10926      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10927      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10928      */
10929     unselect : function(nodeInfo, keepExisting, suppressEvent)
10930     {
10931         if(nodeInfo instanceof Array){
10932             Roo.each(this.selections, function(s) {
10933                 this.unselect(s, nodeInfo);
10934             }, this);
10935             return;
10936         }
10937         var node = this.getNode(nodeInfo);
10938         if(!node || !this.isSelected(node)){
10939             Roo.log("not selected");
10940             return; // not selected.
10941         }
10942         // fireevent???
10943         var ns = [];
10944         Roo.each(this.selections, function(s) {
10945             if (s == node ) {
10946                 Roo.fly(node).removeClass(this.selectedClass);
10947
10948                 return;
10949             }
10950             ns.push(s);
10951         },this);
10952         
10953         this.selections= ns;
10954         this.fireEvent("selectionchange", this, this.selections);
10955     },
10956
10957     /**
10958      * Gets a template node.
10959      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10960      * @return {HTMLElement} The node or null if it wasn't found
10961      */
10962     getNode : function(nodeInfo){
10963         if(typeof nodeInfo == "string"){
10964             return document.getElementById(nodeInfo);
10965         }else if(typeof nodeInfo == "number"){
10966             return this.nodes[nodeInfo];
10967         }
10968         return nodeInfo;
10969     },
10970
10971     /**
10972      * Gets a range template nodes.
10973      * @param {Number} startIndex
10974      * @param {Number} endIndex
10975      * @return {Array} An array of nodes
10976      */
10977     getNodes : function(start, end){
10978         var ns = this.nodes;
10979         start = start || 0;
10980         end = typeof end == "undefined" ? ns.length - 1 : end;
10981         var nodes = [];
10982         if(start <= end){
10983             for(var i = start; i <= end; i++){
10984                 nodes.push(ns[i]);
10985             }
10986         } else{
10987             for(var i = start; i >= end; i--){
10988                 nodes.push(ns[i]);
10989             }
10990         }
10991         return nodes;
10992     },
10993
10994     /**
10995      * Finds the index of the passed node
10996      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10997      * @return {Number} The index of the node or -1
10998      */
10999     indexOf : function(node){
11000         node = this.getNode(node);
11001         if(typeof node.nodeIndex == "number"){
11002             return node.nodeIndex;
11003         }
11004         var ns = this.nodes;
11005         for(var i = 0, len = ns.length; i < len; i++){
11006             if(ns[i] == node){
11007                 return i;
11008             }
11009         }
11010         return -1;
11011     }
11012 });
11013 /*
11014  * - LGPL
11015  *
11016  * based on jquery fullcalendar
11017  * 
11018  */
11019
11020 Roo.bootstrap = Roo.bootstrap || {};
11021 /**
11022  * @class Roo.bootstrap.Calendar
11023  * @extends Roo.bootstrap.Component
11024  * Bootstrap Calendar class
11025  * @cfg {Boolean} loadMask (true|false) default false
11026  * @cfg {Object} header generate the user specific header of the calendar, default false
11027
11028  * @constructor
11029  * Create a new Container
11030  * @param {Object} config The config object
11031  */
11032
11033
11034
11035 Roo.bootstrap.Calendar = function(config){
11036     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11037      this.addEvents({
11038         /**
11039              * @event select
11040              * Fires when a date is selected
11041              * @param {DatePicker} this
11042              * @param {Date} date The selected date
11043              */
11044         'select': true,
11045         /**
11046              * @event monthchange
11047              * Fires when the displayed month changes 
11048              * @param {DatePicker} this
11049              * @param {Date} date The selected month
11050              */
11051         'monthchange': true,
11052         /**
11053              * @event evententer
11054              * Fires when mouse over an event
11055              * @param {Calendar} this
11056              * @param {event} Event
11057              */
11058         'evententer': true,
11059         /**
11060              * @event eventleave
11061              * Fires when the mouse leaves an
11062              * @param {Calendar} this
11063              * @param {event}
11064              */
11065         'eventleave': true,
11066         /**
11067              * @event eventclick
11068              * Fires when the mouse click an
11069              * @param {Calendar} this
11070              * @param {event}
11071              */
11072         'eventclick': true
11073         
11074     });
11075
11076 };
11077
11078 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11079     
11080      /**
11081      * @cfg {Number} startDay
11082      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11083      */
11084     startDay : 0,
11085     
11086     loadMask : false,
11087     
11088     header : false,
11089       
11090     getAutoCreate : function(){
11091         
11092         
11093         var fc_button = function(name, corner, style, content ) {
11094             return Roo.apply({},{
11095                 tag : 'span',
11096                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11097                          (corner.length ?
11098                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11099                             ''
11100                         ),
11101                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11102                 unselectable: 'on'
11103             });
11104         };
11105         
11106         var header = {};
11107         
11108         if(!this.header){
11109             header = {
11110                 tag : 'table',
11111                 cls : 'fc-header',
11112                 style : 'width:100%',
11113                 cn : [
11114                     {
11115                         tag: 'tr',
11116                         cn : [
11117                             {
11118                                 tag : 'td',
11119                                 cls : 'fc-header-left',
11120                                 cn : [
11121                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11122                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11123                                     { tag: 'span', cls: 'fc-header-space' },
11124                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11125
11126
11127                                 ]
11128                             },
11129
11130                             {
11131                                 tag : 'td',
11132                                 cls : 'fc-header-center',
11133                                 cn : [
11134                                     {
11135                                         tag: 'span',
11136                                         cls: 'fc-header-title',
11137                                         cn : {
11138                                             tag: 'H2',
11139                                             html : 'month / year'
11140                                         }
11141                                     }
11142
11143                                 ]
11144                             },
11145                             {
11146                                 tag : 'td',
11147                                 cls : 'fc-header-right',
11148                                 cn : [
11149                               /*      fc_button('month', 'left', '', 'month' ),
11150                                     fc_button('week', '', '', 'week' ),
11151                                     fc_button('day', 'right', '', 'day' )
11152                                 */    
11153
11154                                 ]
11155                             }
11156
11157                         ]
11158                     }
11159                 ]
11160             };
11161         }
11162         
11163         header = this.header;
11164         
11165        
11166         var cal_heads = function() {
11167             var ret = [];
11168             // fixme - handle this.
11169             
11170             for (var i =0; i < Date.dayNames.length; i++) {
11171                 var d = Date.dayNames[i];
11172                 ret.push({
11173                     tag: 'th',
11174                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11175                     html : d.substring(0,3)
11176                 });
11177                 
11178             }
11179             ret[0].cls += ' fc-first';
11180             ret[6].cls += ' fc-last';
11181             return ret;
11182         };
11183         var cal_cell = function(n) {
11184             return  {
11185                 tag: 'td',
11186                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11187                 cn : [
11188                     {
11189                         cn : [
11190                             {
11191                                 cls: 'fc-day-number',
11192                                 html: 'D'
11193                             },
11194                             {
11195                                 cls: 'fc-day-content',
11196                              
11197                                 cn : [
11198                                      {
11199                                         style: 'position: relative;' // height: 17px;
11200                                     }
11201                                 ]
11202                             }
11203                             
11204                             
11205                         ]
11206                     }
11207                 ]
11208                 
11209             }
11210         };
11211         var cal_rows = function() {
11212             
11213             var ret = []
11214             for (var r = 0; r < 6; r++) {
11215                 var row= {
11216                     tag : 'tr',
11217                     cls : 'fc-week',
11218                     cn : []
11219                 };
11220                 
11221                 for (var i =0; i < Date.dayNames.length; i++) {
11222                     var d = Date.dayNames[i];
11223                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11224
11225                 }
11226                 row.cn[0].cls+=' fc-first';
11227                 row.cn[0].cn[0].style = 'min-height:90px';
11228                 row.cn[6].cls+=' fc-last';
11229                 ret.push(row);
11230                 
11231             }
11232             ret[0].cls += ' fc-first';
11233             ret[4].cls += ' fc-prev-last';
11234             ret[5].cls += ' fc-last';
11235             return ret;
11236             
11237         };
11238         
11239         var cal_table = {
11240             tag: 'table',
11241             cls: 'fc-border-separate',
11242             style : 'width:100%',
11243             cellspacing  : 0,
11244             cn : [
11245                 { 
11246                     tag: 'thead',
11247                     cn : [
11248                         { 
11249                             tag: 'tr',
11250                             cls : 'fc-first fc-last',
11251                             cn : cal_heads()
11252                         }
11253                     ]
11254                 },
11255                 { 
11256                     tag: 'tbody',
11257                     cn : cal_rows()
11258                 }
11259                   
11260             ]
11261         };
11262          
11263          var cfg = {
11264             cls : 'fc fc-ltr',
11265             cn : [
11266                 header,
11267                 {
11268                     cls : 'fc-content',
11269                     style : "position: relative;",
11270                     cn : [
11271                         {
11272                             cls : 'fc-view fc-view-month fc-grid',
11273                             style : 'position: relative',
11274                             unselectable : 'on',
11275                             cn : [
11276                                 {
11277                                     cls : 'fc-event-container',
11278                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11279                                 },
11280                                 cal_table
11281                             ]
11282                         }
11283                     ]
11284     
11285                 }
11286            ] 
11287             
11288         };
11289         
11290          
11291         
11292         return cfg;
11293     },
11294     
11295     
11296     initEvents : function()
11297     {
11298         if(!this.store){
11299             throw "can not find store for calendar";
11300         }
11301         
11302         var mark = {
11303             tag: "div",
11304             cls:"x-dlg-mask",
11305             style: "text-align:center",
11306             cn: [
11307                 {
11308                     tag: "div",
11309                     style: "background-color:white;width:50%;margin:250 auto",
11310                     cn: [
11311                         {
11312                             tag: "img",
11313                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11314                         },
11315                         {
11316                             tag: "span",
11317                             html: "Loading"
11318                         }
11319                         
11320                     ]
11321                 }
11322             ]
11323         }
11324         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11325         
11326         var size = this.el.select('.fc-content', true).first().getSize();
11327         this.maskEl.setSize(size.width, size.height);
11328         this.maskEl.enableDisplayMode("block");
11329         if(!this.loadMask){
11330             this.maskEl.hide();
11331         }
11332         
11333         this.store = Roo.factory(this.store, Roo.data);
11334         this.store.on('load', this.onLoad, this);
11335         this.store.on('beforeload', this.onBeforeLoad, this);
11336         
11337         this.resize();
11338         
11339         this.cells = this.el.select('.fc-day',true);
11340         //Roo.log(this.cells);
11341         this.textNodes = this.el.query('.fc-day-number');
11342         this.cells.addClassOnOver('fc-state-hover');
11343         
11344         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11345         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11346         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11347         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11348         
11349         this.on('monthchange', this.onMonthChange, this);
11350         
11351         this.update(new Date().clearTime());
11352     },
11353     
11354     resize : function() {
11355         var sz  = this.el.getSize();
11356         
11357         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11358         this.el.select('.fc-day-content div',true).setHeight(34);
11359     },
11360     
11361     
11362     // private
11363     showPrevMonth : function(e){
11364         this.update(this.activeDate.add("mo", -1));
11365     },
11366     showToday : function(e){
11367         this.update(new Date().clearTime());
11368     },
11369     // private
11370     showNextMonth : function(e){
11371         this.update(this.activeDate.add("mo", 1));
11372     },
11373
11374     // private
11375     showPrevYear : function(){
11376         this.update(this.activeDate.add("y", -1));
11377     },
11378
11379     // private
11380     showNextYear : function(){
11381         this.update(this.activeDate.add("y", 1));
11382     },
11383
11384     
11385    // private
11386     update : function(date)
11387     {
11388         var vd = this.activeDate;
11389         this.activeDate = date;
11390 //        if(vd && this.el){
11391 //            var t = date.getTime();
11392 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11393 //                Roo.log('using add remove');
11394 //                
11395 //                this.fireEvent('monthchange', this, date);
11396 //                
11397 //                this.cells.removeClass("fc-state-highlight");
11398 //                this.cells.each(function(c){
11399 //                   if(c.dateValue == t){
11400 //                       c.addClass("fc-state-highlight");
11401 //                       setTimeout(function(){
11402 //                            try{c.dom.firstChild.focus();}catch(e){}
11403 //                       }, 50);
11404 //                       return false;
11405 //                   }
11406 //                   return true;
11407 //                });
11408 //                return;
11409 //            }
11410 //        }
11411         
11412         var days = date.getDaysInMonth();
11413         
11414         var firstOfMonth = date.getFirstDateOfMonth();
11415         var startingPos = firstOfMonth.getDay()-this.startDay;
11416         
11417         if(startingPos < this.startDay){
11418             startingPos += 7;
11419         }
11420         
11421         var pm = date.add(Date.MONTH, -1);
11422         var prevStart = pm.getDaysInMonth()-startingPos;
11423 //        
11424         this.cells = this.el.select('.fc-day',true);
11425         this.textNodes = this.el.query('.fc-day-number');
11426         this.cells.addClassOnOver('fc-state-hover');
11427         
11428         var cells = this.cells.elements;
11429         var textEls = this.textNodes;
11430         
11431         Roo.each(cells, function(cell){
11432             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11433         });
11434         
11435         days += startingPos;
11436
11437         // convert everything to numbers so it's fast
11438         var day = 86400000;
11439         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11440         //Roo.log(d);
11441         //Roo.log(pm);
11442         //Roo.log(prevStart);
11443         
11444         var today = new Date().clearTime().getTime();
11445         var sel = date.clearTime().getTime();
11446         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11447         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11448         var ddMatch = this.disabledDatesRE;
11449         var ddText = this.disabledDatesText;
11450         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11451         var ddaysText = this.disabledDaysText;
11452         var format = this.format;
11453         
11454         var setCellClass = function(cal, cell){
11455             
11456             //Roo.log('set Cell Class');
11457             cell.title = "";
11458             var t = d.getTime();
11459             
11460             //Roo.log(d);
11461             
11462             cell.dateValue = t;
11463             if(t == today){
11464                 cell.className += " fc-today";
11465                 cell.className += " fc-state-highlight";
11466                 cell.title = cal.todayText;
11467             }
11468             if(t == sel){
11469                 // disable highlight in other month..
11470                 //cell.className += " fc-state-highlight";
11471                 
11472             }
11473             // disabling
11474             if(t < min) {
11475                 cell.className = " fc-state-disabled";
11476                 cell.title = cal.minText;
11477                 return;
11478             }
11479             if(t > max) {
11480                 cell.className = " fc-state-disabled";
11481                 cell.title = cal.maxText;
11482                 return;
11483             }
11484             if(ddays){
11485                 if(ddays.indexOf(d.getDay()) != -1){
11486                     cell.title = ddaysText;
11487                     cell.className = " fc-state-disabled";
11488                 }
11489             }
11490             if(ddMatch && format){
11491                 var fvalue = d.dateFormat(format);
11492                 if(ddMatch.test(fvalue)){
11493                     cell.title = ddText.replace("%0", fvalue);
11494                     cell.className = " fc-state-disabled";
11495                 }
11496             }
11497             
11498             if (!cell.initialClassName) {
11499                 cell.initialClassName = cell.dom.className;
11500             }
11501             
11502             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11503         };
11504
11505         var i = 0;
11506         
11507         for(; i < startingPos; i++) {
11508             textEls[i].innerHTML = (++prevStart);
11509             d.setDate(d.getDate()+1);
11510             
11511             cells[i].className = "fc-past fc-other-month";
11512             setCellClass(this, cells[i]);
11513         }
11514         
11515         var intDay = 0;
11516         
11517         for(; i < days; i++){
11518             intDay = i - startingPos + 1;
11519             textEls[i].innerHTML = (intDay);
11520             d.setDate(d.getDate()+1);
11521             
11522             cells[i].className = ''; // "x-date-active";
11523             setCellClass(this, cells[i]);
11524         }
11525         var extraDays = 0;
11526         
11527         for(; i < 42; i++) {
11528             textEls[i].innerHTML = (++extraDays);
11529             d.setDate(d.getDate()+1);
11530             
11531             cells[i].className = "fc-future fc-other-month";
11532             setCellClass(this, cells[i]);
11533         }
11534         
11535         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11536         
11537         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11538         
11539         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11540         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11541         
11542         if(totalRows != 6){
11543             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11544             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11545         }
11546         
11547         this.fireEvent('monthchange', this, date);
11548         
11549         
11550         /*
11551         if(!this.internalRender){
11552             var main = this.el.dom.firstChild;
11553             var w = main.offsetWidth;
11554             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11555             Roo.fly(main).setWidth(w);
11556             this.internalRender = true;
11557             // opera does not respect the auto grow header center column
11558             // then, after it gets a width opera refuses to recalculate
11559             // without a second pass
11560             if(Roo.isOpera && !this.secondPass){
11561                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11562                 this.secondPass = true;
11563                 this.update.defer(10, this, [date]);
11564             }
11565         }
11566         */
11567         
11568     },
11569     
11570     findCell : function(dt) {
11571         dt = dt.clearTime().getTime();
11572         var ret = false;
11573         this.cells.each(function(c){
11574             //Roo.log("check " +c.dateValue + '?=' + dt);
11575             if(c.dateValue == dt){
11576                 ret = c;
11577                 return false;
11578             }
11579             return true;
11580         });
11581         
11582         return ret;
11583     },
11584     
11585     findCells : function(ev) {
11586         var s = ev.start.clone().clearTime().getTime();
11587        // Roo.log(s);
11588         var e= ev.end.clone().clearTime().getTime();
11589        // Roo.log(e);
11590         var ret = [];
11591         this.cells.each(function(c){
11592              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11593             
11594             if(c.dateValue > e){
11595                 return ;
11596             }
11597             if(c.dateValue < s){
11598                 return ;
11599             }
11600             ret.push(c);
11601         });
11602         
11603         return ret;    
11604     },
11605     
11606 //    findBestRow: function(cells)
11607 //    {
11608 //        var ret = 0;
11609 //        
11610 //        for (var i =0 ; i < cells.length;i++) {
11611 //            ret  = Math.max(cells[i].rows || 0,ret);
11612 //        }
11613 //        return ret;
11614 //        
11615 //    },
11616     
11617     
11618     addItem : function(ev)
11619     {
11620         // look for vertical location slot in
11621         var cells = this.findCells(ev);
11622         
11623 //        ev.row = this.findBestRow(cells);
11624         
11625         // work out the location.
11626         
11627         var crow = false;
11628         var rows = [];
11629         for(var i =0; i < cells.length; i++) {
11630             if (!crow) {
11631                 crow = {
11632                     start : cells[i],
11633                     end :  cells[i]
11634                 };
11635                 continue;
11636             }
11637             if (crow.start.getY() == cells[i].getY()) {
11638                 // on same row.
11639                 crow.end = cells[i];
11640                 continue;
11641             }
11642             // different row.
11643             rows.push(crow);
11644             crow = {
11645                 start: cells[i],
11646                 end : cells[i]
11647             };
11648             
11649         }
11650         
11651         rows.push(crow);
11652         ev.els = [];
11653         ev.rows = rows;
11654         ev.cells = cells;
11655         ev.rendered = false;
11656 //        for (var i = 0; i < cells.length;i++) {
11657 //            cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11658 //            
11659 //        }
11660         
11661         this.calevents.push(ev);
11662     },
11663     
11664     clearEvents: function() {
11665         
11666         if(!this.calevents){
11667             return;
11668         }
11669         
11670         Roo.each(this.cells.elements, function(c){
11671             c.rows = [];
11672             c.more = [];
11673         });
11674         
11675         Roo.each(this.calevents, function(e) {
11676             Roo.each(e.els, function(el) {
11677                 el.un('mouseenter' ,this.onEventEnter, this);
11678                 el.un('mouseleave' ,this.onEventLeave, this);
11679                 el.remove();
11680             },this);
11681         },this);
11682         
11683         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11684             e.remove();
11685         });
11686         
11687     },
11688     
11689     renderEvents: function()
11690     {   
11691         // first make sure there is enough space..
11692         this.cells.each(function(c) {
11693             c.rows = [];
11694             c.more = [];
11695         });
11696         
11697         for (var e = 0; e < this.calevents.length; e++) {
11698             
11699             var ev = this.calevents[e];
11700             var cells = ev.cells;
11701             var rows = ev.rows;
11702             
11703             for(var i = 0; i < cells.length; i++){
11704                 
11705                 var cbox = this.cells.item(this.cells.indexOf(cells[i]));
11706                 
11707                 if(cells.length < 2 && cbox.rows.length > 3){
11708                     cbox.more.push(ev);
11709                     continue;
11710                 }
11711                 
11712                 cbox.rows.push(ev);
11713             }
11714         }
11715         
11716         var _this = this;
11717         
11718         this.cells.each(function(c) {
11719             if(c.more.length && c.more.length == 1){
11720                 c.rows.push(c.more.pop());
11721             }
11722             
11723             var r = (c.more.length) ? c.rows.length + 1 : c.rows.length;
11724             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, r * 20));
11725             
11726             
11727             for (var e = 0; e < c.rows.length; e++){
11728                 var ev = c.rows[e];
11729                 
11730                 if(ev.rendered){
11731                     continue;
11732                 }
11733                 
11734                 var cells = ev.cells;
11735                 var rows = ev.rows;
11736                 
11737                 for(var i = 0; i < rows.length; i++) {
11738                 
11739                     // how many rows should it span..
11740
11741                     var  cfg = {
11742                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11743                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11744
11745                         unselectable : "on",
11746                         cn : [
11747                             {
11748                                 cls: 'fc-event-inner',
11749                                 cn : [
11750     //                                {
11751     //                                  tag:'span',
11752     //                                  cls: 'fc-event-time',
11753     //                                  html : cells.length > 1 ? '' : ev.time
11754     //                                },
11755                                     {
11756                                       tag:'span',
11757                                       cls: 'fc-event-title',
11758                                       html : String.format('{0}', ev.title)
11759                                     }
11760
11761
11762                                 ]
11763                             },
11764                             {
11765                                 cls: 'ui-resizable-handle ui-resizable-e',
11766                                 html : '&nbsp;&nbsp;&nbsp'
11767                             }
11768
11769                         ]
11770                     };
11771
11772                     if (i == 0) {
11773                         cfg.cls += ' fc-event-start';
11774                     }
11775                     if ((i+1) == rows.length) {
11776                         cfg.cls += ' fc-event-end';
11777                     }
11778
11779                     var ctr = _this.el.select('.fc-event-container',true).first();
11780                     var cg = ctr.createChild(cfg);
11781
11782                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11783                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11784
11785                     cg.setXY([sbox.x +2, sbox.y +(e * 20)]);    
11786                     cg.setWidth(ebox.right - sbox.x -2);
11787
11788                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11789                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11790                     cg.on('click', _this.onEventClick, _this, ev);
11791
11792                     ev.els.push(cg);
11793                     
11794                     ev.rendered = true;
11795                 }
11796                 
11797             }
11798             
11799             
11800             if(c.more.length){
11801                 var  cfg = {
11802                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11803                     style : 'position: absolute',
11804                     unselectable : "on",
11805                     cn : [
11806                         {
11807                             cls: 'fc-event-inner',
11808                             cn : [
11809                                 {
11810                                   tag:'span',
11811                                   cls: 'fc-event-title',
11812                                   html : 'More'
11813                                 }
11814
11815
11816                             ]
11817                         },
11818                         {
11819                             cls: 'ui-resizable-handle ui-resizable-e',
11820                             html : '&nbsp;&nbsp;&nbsp'
11821                         }
11822
11823                     ]
11824                 };
11825
11826                 var ctr = _this.el.select('.fc-event-container',true).first();
11827                 var cg = ctr.createChild(cfg);
11828
11829                 var sbox = c.select('.fc-day-content',true).first().getBox();
11830                 var ebox = c.select('.fc-day-content',true).first().getBox();
11831                 //Roo.log(cg);
11832                 cg.setXY([sbox.x +2, sbox.y +(c.rows.length * 20)]);    
11833                 cg.setWidth(ebox.right - sbox.x -2);
11834
11835                 cg.on('click', _this.onMoreEventClick, _this, c.more);
11836                 
11837             }
11838             
11839         });
11840         
11841         
11842         
11843     },
11844     
11845     onEventEnter: function (e, el,event,d) {
11846         this.fireEvent('evententer', this, el, event);
11847     },
11848     
11849     onEventLeave: function (e, el,event,d) {
11850         this.fireEvent('eventleave', this, el, event);
11851     },
11852     
11853     onEventClick: function (e, el,event,d) {
11854         this.fireEvent('eventclick', this, el, event);
11855     },
11856     
11857     onMonthChange: function () {
11858         this.store.load();
11859     },
11860     
11861     onMoreEventClick: function(e, el, more)
11862     {
11863         var _this = this;
11864         
11865         this.calpopover.placement = 'right';
11866         this.calpopover.setTitle('More');
11867         
11868         this.calpopover.setContent('');
11869         
11870         var ctr = this.calpopover.el.select('.popover-content', true).first();
11871         
11872         Roo.each(more, function(m){
11873             var cfg = {
11874                 cls : 'fc-event-hori fc-event-draggable',
11875                 html : m.title
11876             }
11877             var cg = ctr.createChild(cfg);
11878             
11879             cg.on('click', _this.onEventClick, _this, m);
11880         });
11881         
11882         this.calpopover.show(el);
11883         
11884         
11885     },
11886     
11887     onLoad: function () 
11888     {   
11889         this.calevents = [];
11890         var cal = this;
11891         
11892         if(this.store.getCount() > 0){
11893             this.store.data.each(function(d){
11894                cal.addItem({
11895                     id : d.data.id,
11896                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11897                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11898                     time : d.data.start_time,
11899                     title : d.data.title,
11900                     description : d.data.description,
11901                     venue : d.data.venue
11902                 });
11903             });
11904         }
11905         
11906         this.renderEvents();
11907         
11908         if(this.calevents.length && this.loadMask){
11909             this.maskEl.hide();
11910         }
11911     },
11912     
11913     onBeforeLoad: function()
11914     {
11915         this.clearEvents();
11916         if(this.loadMask){
11917             this.maskEl.show();
11918         }
11919     }
11920 });
11921
11922  
11923  /*
11924  * - LGPL
11925  *
11926  * element
11927  * 
11928  */
11929
11930 /**
11931  * @class Roo.bootstrap.Popover
11932  * @extends Roo.bootstrap.Component
11933  * Bootstrap Popover class
11934  * @cfg {String} html contents of the popover   (or false to use children..)
11935  * @cfg {String} title of popover (or false to hide)
11936  * @cfg {String} placement how it is placed
11937  * @cfg {String} trigger click || hover (or false to trigger manually)
11938  * @cfg {String} over what (parent or false to trigger manually.)
11939  * 
11940  * @constructor
11941  * Create a new Popover
11942  * @param {Object} config The config object
11943  */
11944
11945 Roo.bootstrap.Popover = function(config){
11946     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11947 };
11948
11949 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11950     
11951     title: 'Fill in a title',
11952     html: false,
11953     
11954     placement : 'right',
11955     trigger : 'hover', // hover
11956     
11957     over: 'parent',
11958     
11959     can_build_overlaid : false,
11960     
11961     getChildContainer : function()
11962     {
11963         return this.el.select('.popover-content',true).first();
11964     },
11965     
11966     getAutoCreate : function(){
11967          Roo.log('make popover?');
11968         var cfg = {
11969            cls : 'popover roo-dynamic',
11970            style: 'display:block',
11971            cn : [
11972                 {
11973                     cls : 'arrow'
11974                 },
11975                 {
11976                     cls : 'popover-inner',
11977                     cn : [
11978                         {
11979                             tag: 'h3',
11980                             cls: 'popover-title',
11981                             html : this.title
11982                         },
11983                         {
11984                             cls : 'popover-content',
11985                             html : this.html
11986                         }
11987                     ]
11988                     
11989                 }
11990            ]
11991         };
11992         
11993         return cfg;
11994     },
11995     setTitle: function(str)
11996     {
11997         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11998     },
11999     setContent: function(str)
12000     {
12001         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12002     },
12003     // as it get's added to the bottom of the page.
12004     onRender : function(ct, position)
12005     {
12006         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12007         if(!this.el){
12008             var cfg = Roo.apply({},  this.getAutoCreate());
12009             cfg.id = Roo.id();
12010             
12011             if (this.cls) {
12012                 cfg.cls += ' ' + this.cls;
12013             }
12014             if (this.style) {
12015                 cfg.style = this.style;
12016             }
12017             Roo.log("adding to ")
12018             this.el = Roo.get(document.body).createChild(cfg, position);
12019             Roo.log(this.el);
12020         }
12021         this.initEvents();
12022     },
12023     
12024     initEvents : function()
12025     {
12026         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12027         this.el.enableDisplayMode('block');
12028         this.el.hide();
12029         if (this.over === false) {
12030             return; 
12031         }
12032         if (this.triggers === false) {
12033             return;
12034         }
12035         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12036         var triggers = this.trigger ? this.trigger.split(' ') : [];
12037         Roo.each(triggers, function(trigger) {
12038         
12039             if (trigger == 'click') {
12040                 on_el.on('click', this.toggle, this);
12041             } else if (trigger != 'manual') {
12042                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12043                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12044       
12045                 on_el.on(eventIn  ,this.enter, this);
12046                 on_el.on(eventOut, this.leave, this);
12047             }
12048         }, this);
12049         
12050     },
12051     
12052     
12053     // private
12054     timeout : null,
12055     hoverState : null,
12056     
12057     toggle : function () {
12058         this.hoverState == 'in' ? this.leave() : this.enter();
12059     },
12060     
12061     enter : function () {
12062        
12063     
12064         clearTimeout(this.timeout);
12065     
12066         this.hoverState = 'in'
12067     
12068         if (!this.delay || !this.delay.show) {
12069             this.show();
12070             return 
12071         }
12072         var _t = this;
12073         this.timeout = setTimeout(function () {
12074             if (_t.hoverState == 'in') {
12075                 _t.show();
12076             }
12077         }, this.delay.show)
12078     },
12079     leave : function() {
12080         clearTimeout(this.timeout);
12081     
12082         this.hoverState = 'out'
12083     
12084         if (!this.delay || !this.delay.hide) {
12085             this.hide();
12086             return 
12087         }
12088         var _t = this;
12089         this.timeout = setTimeout(function () {
12090             if (_t.hoverState == 'out') {
12091                 _t.hide();
12092             }
12093         }, this.delay.hide)
12094     },
12095     
12096     show : function (on_el)
12097     {
12098         if (!on_el) {
12099             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12100         }
12101         // set content.
12102         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12103         if (this.html !== false) {
12104             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12105         }
12106         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12107         if (!this.title.length) {
12108             this.el.select('.popover-title',true).hide();
12109         }
12110         
12111         var placement = typeof this.placement == 'function' ?
12112             this.placement.call(this, this.el, on_el) :
12113             this.placement;
12114             
12115         var autoToken = /\s?auto?\s?/i;
12116         var autoPlace = autoToken.test(placement);
12117         if (autoPlace) {
12118             placement = placement.replace(autoToken, '') || 'top';
12119         }
12120         
12121         //this.el.detach()
12122         //this.el.setXY([0,0]);
12123         this.el.show();
12124         this.el.dom.style.display='block';
12125         this.el.addClass(placement);
12126         
12127         //this.el.appendTo(on_el);
12128         
12129         var p = this.getPosition();
12130         var box = this.el.getBox();
12131         
12132         if (autoPlace) {
12133             // fixme..
12134         }
12135         var align = Roo.bootstrap.Popover.alignment[placement]
12136         this.el.alignTo(on_el, align[0],align[1]);
12137         //var arrow = this.el.select('.arrow',true).first();
12138         //arrow.set(align[2], 
12139         
12140         this.el.addClass('in');
12141         this.hoverState = null;
12142         
12143         if (this.el.hasClass('fade')) {
12144             // fade it?
12145         }
12146         
12147     },
12148     hide : function()
12149     {
12150         this.el.setXY([0,0]);
12151         this.el.removeClass('in');
12152         this.el.hide();
12153         
12154     }
12155     
12156 });
12157
12158 Roo.bootstrap.Popover.alignment = {
12159     'left' : ['r-l', [-10,0], 'right'],
12160     'right' : ['l-r', [10,0], 'left'],
12161     'bottom' : ['t-b', [0,10], 'top'],
12162     'top' : [ 'b-t', [0,-10], 'bottom']
12163 };
12164
12165  /*
12166  * - LGPL
12167  *
12168  * Progress
12169  * 
12170  */
12171
12172 /**
12173  * @class Roo.bootstrap.Progress
12174  * @extends Roo.bootstrap.Component
12175  * Bootstrap Progress class
12176  * @cfg {Boolean} striped striped of the progress bar
12177  * @cfg {Boolean} active animated of the progress bar
12178  * 
12179  * 
12180  * @constructor
12181  * Create a new Progress
12182  * @param {Object} config The config object
12183  */
12184
12185 Roo.bootstrap.Progress = function(config){
12186     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12187 };
12188
12189 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12190     
12191     striped : false,
12192     active: false,
12193     
12194     getAutoCreate : function(){
12195         var cfg = {
12196             tag: 'div',
12197             cls: 'progress'
12198         };
12199         
12200         
12201         if(this.striped){
12202             cfg.cls += ' progress-striped';
12203         }
12204       
12205         if(this.active){
12206             cfg.cls += ' active';
12207         }
12208         
12209         
12210         return cfg;
12211     }
12212    
12213 });
12214
12215  
12216
12217  /*
12218  * - LGPL
12219  *
12220  * ProgressBar
12221  * 
12222  */
12223
12224 /**
12225  * @class Roo.bootstrap.ProgressBar
12226  * @extends Roo.bootstrap.Component
12227  * Bootstrap ProgressBar class
12228  * @cfg {Number} aria_valuenow aria-value now
12229  * @cfg {Number} aria_valuemin aria-value min
12230  * @cfg {Number} aria_valuemax aria-value max
12231  * @cfg {String} label label for the progress bar
12232  * @cfg {String} panel (success | info | warning | danger )
12233  * @cfg {String} role role of the progress bar
12234  * @cfg {String} sr_only text
12235  * 
12236  * 
12237  * @constructor
12238  * Create a new ProgressBar
12239  * @param {Object} config The config object
12240  */
12241
12242 Roo.bootstrap.ProgressBar = function(config){
12243     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12244 };
12245
12246 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12247     
12248     aria_valuenow : 0,
12249     aria_valuemin : 0,
12250     aria_valuemax : 100,
12251     label : false,
12252     panel : false,
12253     role : false,
12254     sr_only: false,
12255     
12256     getAutoCreate : function()
12257     {
12258         
12259         var cfg = {
12260             tag: 'div',
12261             cls: 'progress-bar',
12262             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12263         };
12264         
12265         if(this.sr_only){
12266             cfg.cn = {
12267                 tag: 'span',
12268                 cls: 'sr-only',
12269                 html: this.sr_only
12270             }
12271         }
12272         
12273         if(this.role){
12274             cfg.role = this.role;
12275         }
12276         
12277         if(this.aria_valuenow){
12278             cfg['aria-valuenow'] = this.aria_valuenow;
12279         }
12280         
12281         if(this.aria_valuemin){
12282             cfg['aria-valuemin'] = this.aria_valuemin;
12283         }
12284         
12285         if(this.aria_valuemax){
12286             cfg['aria-valuemax'] = this.aria_valuemax;
12287         }
12288         
12289         if(this.label && !this.sr_only){
12290             cfg.html = this.label;
12291         }
12292         
12293         if(this.panel){
12294             cfg.cls += ' progress-bar-' + this.panel;
12295         }
12296         
12297         return cfg;
12298     },
12299     
12300     update : function(aria_valuenow)
12301     {
12302         this.aria_valuenow = aria_valuenow;
12303         
12304         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12305     }
12306    
12307 });
12308
12309  
12310
12311  /*
12312  * - LGPL
12313  *
12314  * TabPanel
12315  * 
12316  */
12317
12318 /**
12319  * @class Roo.bootstrap.TabPanel
12320  * @extends Roo.bootstrap.Component
12321  * Bootstrap TabPanel class
12322  * @cfg {Boolean} active panel active
12323  * @cfg {String} html panel content
12324  * @cfg {String} tabId tab relate id
12325  * @cfg {String} navId The navbar which triggers show hide
12326  * 
12327  * 
12328  * @constructor
12329  * Create a new TabPanel
12330  * @param {Object} config The config object
12331  */
12332
12333 Roo.bootstrap.TabPanel = function(config){
12334     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12335      this.addEvents({
12336         /**
12337              * @event changed
12338              * Fires when the active status changes
12339              * @param {Roo.bootstrap.TabPanel} this
12340              * @param {Boolean} state the new state
12341             
12342          */
12343         'changed': true
12344      });
12345 };
12346
12347 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12348     
12349     active: false,
12350     html: false,
12351     tabId: false,
12352     navId : false,
12353     
12354     getAutoCreate : function(){
12355         var cfg = {
12356             tag: 'div',
12357             cls: 'tab-pane',
12358             html: this.html || ''
12359         };
12360         
12361         if(this.active){
12362             cfg.cls += ' active';
12363         }
12364         
12365         if(this.tabId){
12366             cfg.tabId = this.tabId;
12367         }
12368         
12369         return cfg;
12370     },
12371     onRender : function(ct, position)
12372     {
12373        // Roo.log("Call onRender: " + this.xtype);
12374         
12375         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12376         
12377         if (this.navId && this.tabId) {
12378             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12379             if (!item) {
12380                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12381             } else {
12382                 item.on('changed', function(item, state) {
12383                     this.setActive(state);
12384                 }, this);
12385             }
12386         }
12387         
12388     },
12389     setActive: function(state)
12390     {
12391         Roo.log("panel - set active " + this.tabId + "=" + state);
12392         
12393         this.active = state;
12394         if (!state) {
12395             this.el.removeClass('active');
12396             
12397         } else  if (!this.el.hasClass('active')) {
12398             this.el.addClass('active');
12399         }
12400         this.fireEvent('changed', this, state);
12401     }
12402     
12403     
12404 });
12405  
12406
12407  
12408
12409  /*
12410  * - LGPL
12411  *
12412  * DateField
12413  * 
12414  */
12415
12416 /**
12417  * @class Roo.bootstrap.DateField
12418  * @extends Roo.bootstrap.Input
12419  * Bootstrap DateField class
12420  * @cfg {Number} weekStart default 0
12421  * @cfg {Number} weekStart default 0
12422  * @cfg {Number} viewMode default empty, (months|years)
12423  * @cfg {Number} minViewMode default empty, (months|years)
12424  * @cfg {Number} startDate default -Infinity
12425  * @cfg {Number} endDate default Infinity
12426  * @cfg {Boolean} todayHighlight default false
12427  * @cfg {Boolean} todayBtn default false
12428  * @cfg {Boolean} calendarWeeks default false
12429  * @cfg {Object} daysOfWeekDisabled default empty
12430  * 
12431  * @cfg {Boolean} keyboardNavigation default true
12432  * @cfg {String} language default en
12433  * 
12434  * @constructor
12435  * Create a new DateField
12436  * @param {Object} config The config object
12437  */
12438
12439 Roo.bootstrap.DateField = function(config){
12440     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12441      this.addEvents({
12442             /**
12443              * @event show
12444              * Fires when this field show.
12445              * @param {Roo.bootstrap.DateField} this
12446              * @param {Mixed} date The date value
12447              */
12448             show : true,
12449             /**
12450              * @event show
12451              * Fires when this field hide.
12452              * @param {Roo.bootstrap.DateField} this
12453              * @param {Mixed} date The date value
12454              */
12455             hide : true,
12456             /**
12457              * @event select
12458              * Fires when select a date.
12459              * @param {Roo.bootstrap.DateField} this
12460              * @param {Mixed} date The date value
12461              */
12462             select : true
12463         });
12464 };
12465
12466 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12467     
12468     /**
12469      * @cfg {String} format
12470      * The default date format string which can be overriden for localization support.  The format must be
12471      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12472      */
12473     format : "m/d/y",
12474     /**
12475      * @cfg {String} altFormats
12476      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12477      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12478      */
12479     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12480     
12481     weekStart : 0,
12482     
12483     viewMode : '',
12484     
12485     minViewMode : '',
12486     
12487     todayHighlight : false,
12488     
12489     todayBtn: false,
12490     
12491     language: 'en',
12492     
12493     keyboardNavigation: true,
12494     
12495     calendarWeeks: false,
12496     
12497     startDate: -Infinity,
12498     
12499     endDate: Infinity,
12500     
12501     daysOfWeekDisabled: [],
12502     
12503     _events: [],
12504     
12505     UTCDate: function()
12506     {
12507         return new Date(Date.UTC.apply(Date, arguments));
12508     },
12509     
12510     UTCToday: function()
12511     {
12512         var today = new Date();
12513         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12514     },
12515     
12516     getDate: function() {
12517             var d = this.getUTCDate();
12518             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12519     },
12520     
12521     getUTCDate: function() {
12522             return this.date;
12523     },
12524     
12525     setDate: function(d) {
12526             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12527     },
12528     
12529     setUTCDate: function(d) {
12530             this.date = d;
12531             this.setValue(this.formatDate(this.date));
12532     },
12533         
12534     onRender: function(ct, position)
12535     {
12536         
12537         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12538         
12539         this.language = this.language || 'en';
12540         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12541         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12542         
12543         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12544         this.format = this.format || 'm/d/y';
12545         this.isInline = false;
12546         this.isInput = true;
12547         this.component = this.el.select('.add-on', true).first() || false;
12548         this.component = (this.component && this.component.length === 0) ? false : this.component;
12549         this.hasInput = this.component && this.inputEL().length;
12550         
12551         if (typeof(this.minViewMode === 'string')) {
12552             switch (this.minViewMode) {
12553                 case 'months':
12554                     this.minViewMode = 1;
12555                     break;
12556                 case 'years':
12557                     this.minViewMode = 2;
12558                     break;
12559                 default:
12560                     this.minViewMode = 0;
12561                     break;
12562             }
12563         }
12564         
12565         if (typeof(this.viewMode === 'string')) {
12566             switch (this.viewMode) {
12567                 case 'months':
12568                     this.viewMode = 1;
12569                     break;
12570                 case 'years':
12571                     this.viewMode = 2;
12572                     break;
12573                 default:
12574                     this.viewMode = 0;
12575                     break;
12576             }
12577         }
12578                 
12579         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12580         
12581         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12582         
12583         this.picker().on('mousedown', this.onMousedown, this);
12584         this.picker().on('click', this.onClick, this);
12585         
12586         this.picker().addClass('datepicker-dropdown');
12587         
12588         this.startViewMode = this.viewMode;
12589         
12590         
12591         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12592             if(!this.calendarWeeks){
12593                 v.remove();
12594                 return;
12595             };
12596             
12597             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12598             v.attr('colspan', function(i, val){
12599                 return parseInt(val) + 1;
12600             });
12601         })
12602                         
12603         
12604         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12605         
12606         this.setStartDate(this.startDate);
12607         this.setEndDate(this.endDate);
12608         
12609         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12610         
12611         this.fillDow();
12612         this.fillMonths();
12613         this.update();
12614         this.showMode();
12615         
12616         if(this.isInline) {
12617             this.show();
12618         }
12619     },
12620     
12621     picker : function()
12622     {
12623         return this.el.select('.datepicker', true).first();
12624     },
12625     
12626     fillDow: function()
12627     {
12628         var dowCnt = this.weekStart;
12629         
12630         var dow = {
12631             tag: 'tr',
12632             cn: [
12633                 
12634             ]
12635         };
12636         
12637         if(this.calendarWeeks){
12638             dow.cn.push({
12639                 tag: 'th',
12640                 cls: 'cw',
12641                 html: '&nbsp;'
12642             })
12643         }
12644         
12645         while (dowCnt < this.weekStart + 7) {
12646             dow.cn.push({
12647                 tag: 'th',
12648                 cls: 'dow',
12649                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12650             });
12651         }
12652         
12653         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12654     },
12655     
12656     fillMonths: function()
12657     {    
12658         var i = 0
12659         var months = this.picker().select('>.datepicker-months td', true).first();
12660         
12661         months.dom.innerHTML = '';
12662         
12663         while (i < 12) {
12664             var month = {
12665                 tag: 'span',
12666                 cls: 'month',
12667                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12668             }
12669             
12670             months.createChild(month);
12671         }
12672         
12673     },
12674     
12675     update: function(){
12676         
12677         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12678         
12679         if (this.date < this.startDate) {
12680             this.viewDate = new Date(this.startDate);
12681         } else if (this.date > this.endDate) {
12682             this.viewDate = new Date(this.endDate);
12683         } else {
12684             this.viewDate = new Date(this.date);
12685         }
12686         
12687         this.fill();
12688     },
12689     
12690     fill: function() {
12691         var d = new Date(this.viewDate),
12692                 year = d.getUTCFullYear(),
12693                 month = d.getUTCMonth(),
12694                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12695                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12696                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12697                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12698                 currentDate = this.date && this.date.valueOf(),
12699                 today = this.UTCToday();
12700         
12701         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12702         
12703 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12704         
12705 //        this.picker.select('>tfoot th.today').
12706 //                                              .text(dates[this.language].today)
12707 //                                              .toggle(this.todayBtn !== false);
12708     
12709         this.updateNavArrows();
12710         this.fillMonths();
12711                                                 
12712         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12713         
12714         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12715          
12716         prevMonth.setUTCDate(day);
12717         
12718         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12719         
12720         var nextMonth = new Date(prevMonth);
12721         
12722         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12723         
12724         nextMonth = nextMonth.valueOf();
12725         
12726         var fillMonths = false;
12727         
12728         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12729         
12730         while(prevMonth.valueOf() < nextMonth) {
12731             var clsName = '';
12732             
12733             if (prevMonth.getUTCDay() === this.weekStart) {
12734                 if(fillMonths){
12735                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12736                 }
12737                     
12738                 fillMonths = {
12739                     tag: 'tr',
12740                     cn: []
12741                 };
12742                 
12743                 if(this.calendarWeeks){
12744                     // ISO 8601: First week contains first thursday.
12745                     // ISO also states week starts on Monday, but we can be more abstract here.
12746                     var
12747                     // Start of current week: based on weekstart/current date
12748                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12749                     // Thursday of this week
12750                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12751                     // First Thursday of year, year from thursday
12752                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12753                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12754                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12755                     
12756                     fillMonths.cn.push({
12757                         tag: 'td',
12758                         cls: 'cw',
12759                         html: calWeek
12760                     });
12761                 }
12762             }
12763             
12764             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12765                 clsName += ' old';
12766             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12767                 clsName += ' new';
12768             }
12769             if (this.todayHighlight &&
12770                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12771                 prevMonth.getUTCMonth() == today.getMonth() &&
12772                 prevMonth.getUTCDate() == today.getDate()) {
12773                 clsName += ' today';
12774             }
12775             
12776             if (currentDate && prevMonth.valueOf() === currentDate) {
12777                 clsName += ' active';
12778             }
12779             
12780             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12781                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12782                     clsName += ' disabled';
12783             }
12784             
12785             fillMonths.cn.push({
12786                 tag: 'td',
12787                 cls: 'day ' + clsName,
12788                 html: prevMonth.getDate()
12789             })
12790             
12791             prevMonth.setDate(prevMonth.getDate()+1);
12792         }
12793           
12794         var currentYear = this.date && this.date.getUTCFullYear();
12795         var currentMonth = this.date && this.date.getUTCMonth();
12796         
12797         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12798         
12799         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12800             v.removeClass('active');
12801             
12802             if(currentYear === year && k === currentMonth){
12803                 v.addClass('active');
12804             }
12805             
12806             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12807                 v.addClass('disabled');
12808             }
12809             
12810         });
12811         
12812         
12813         year = parseInt(year/10, 10) * 10;
12814         
12815         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12816         
12817         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12818         
12819         year -= 1;
12820         for (var i = -1; i < 11; i++) {
12821             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12822                 tag: 'span',
12823                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12824                 html: year
12825             })
12826             
12827             year += 1;
12828         }
12829     },
12830     
12831     showMode: function(dir) {
12832         if (dir) {
12833             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12834         }
12835         Roo.each(this.picker().select('>div',true).elements, function(v){
12836             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12837             v.hide();
12838         });
12839         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12840     },
12841     
12842     place: function()
12843     {
12844         if(this.isInline) return;
12845         
12846         this.picker().removeClass(['bottom', 'top']);
12847         
12848         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12849             /*
12850              * place to the top of element!
12851              *
12852              */
12853             
12854             this.picker().addClass('top');
12855             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12856             
12857             return;
12858         }
12859         
12860         this.picker().addClass('bottom');
12861         
12862         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12863     },
12864     
12865     parseDate : function(value){
12866         if(!value || value instanceof Date){
12867             return value;
12868         }
12869         var v = Date.parseDate(value, this.format);
12870         if (!v && this.useIso) {
12871             v = Date.parseDate(value, 'Y-m-d');
12872         }
12873         if(!v && this.altFormats){
12874             if(!this.altFormatsArray){
12875                 this.altFormatsArray = this.altFormats.split("|");
12876             }
12877             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12878                 v = Date.parseDate(value, this.altFormatsArray[i]);
12879             }
12880         }
12881         return v;
12882     },
12883     
12884     formatDate : function(date, fmt){
12885         return (!date || !(date instanceof Date)) ?
12886         date : date.dateFormat(fmt || this.format);
12887     },
12888     
12889     onFocus : function()
12890     {
12891         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12892         this.show();
12893     },
12894     
12895     onBlur : function()
12896     {
12897         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12898         this.hide();
12899     },
12900     
12901     show : function()
12902     {
12903         this.picker().show();
12904         this.update();
12905         this.place();
12906         
12907         this.fireEvent('show', this, this.date);
12908     },
12909     
12910     hide : function()
12911     {
12912         if(this.isInline) return;
12913         this.picker().hide();
12914         this.viewMode = this.startViewMode;
12915         this.showMode();
12916         
12917         this.fireEvent('hide', this, this.date);
12918         
12919     },
12920     
12921     onMousedown: function(e){
12922         e.stopPropagation();
12923         e.preventDefault();
12924     },
12925     
12926     keyup: function(e){
12927         Roo.bootstrap.DateField.superclass.keyup.call(this);
12928         this.update();
12929         
12930     },
12931
12932     setValue: function(v){
12933         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12934         
12935         this.fireEvent('select', this, this.date);
12936         
12937     },
12938     
12939     fireKey: function(e){
12940         if (!this.picker().isVisible()){
12941             if (e.keyCode == 27) // allow escape to hide and re-show picker
12942                 this.show();
12943             return;
12944         }
12945         var dateChanged = false,
12946         dir, day, month,
12947         newDate, newViewDate;
12948         switch(e.keyCode){
12949             case 27: // escape
12950                 this.hide();
12951                 e.preventDefault();
12952                 break;
12953             case 37: // left
12954             case 39: // right
12955                 if (!this.keyboardNavigation) break;
12956                 dir = e.keyCode == 37 ? -1 : 1;
12957                 
12958                 if (e.ctrlKey){
12959                     newDate = this.moveYear(this.date, dir);
12960                     newViewDate = this.moveYear(this.viewDate, dir);
12961                 } else if (e.shiftKey){
12962                     newDate = this.moveMonth(this.date, dir);
12963                     newViewDate = this.moveMonth(this.viewDate, dir);
12964                 } else {
12965                     newDate = new Date(this.date);
12966                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12967                     newViewDate = new Date(this.viewDate);
12968                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12969                 }
12970                 if (this.dateWithinRange(newDate)){
12971                     this.date = newDate;
12972                     this.viewDate = newViewDate;
12973                     this.setValue(this.formatDate(this.date));
12974                     this.update();
12975                     e.preventDefault();
12976                     dateChanged = true;
12977                 }
12978                 break;
12979             case 38: // up
12980             case 40: // down
12981                 if (!this.keyboardNavigation) break;
12982                 dir = e.keyCode == 38 ? -1 : 1;
12983                 if (e.ctrlKey){
12984                     newDate = this.moveYear(this.date, dir);
12985                     newViewDate = this.moveYear(this.viewDate, dir);
12986                 } else if (e.shiftKey){
12987                     newDate = this.moveMonth(this.date, dir);
12988                     newViewDate = this.moveMonth(this.viewDate, dir);
12989                 } else {
12990                     newDate = new Date(this.date);
12991                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12992                     newViewDate = new Date(this.viewDate);
12993                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12994                 }
12995                 if (this.dateWithinRange(newDate)){
12996                     this.date = newDate;
12997                     this.viewDate = newViewDate;
12998                     this.setValue(this.formatDate(this.date));
12999                     this.update();
13000                     e.preventDefault();
13001                     dateChanged = true;
13002                 }
13003                 break;
13004             case 13: // enter
13005                 this.setValue(this.formatDate(this.date));
13006                 this.hide();
13007                 e.preventDefault();
13008                 break;
13009             case 9: // tab
13010                 this.setValue(this.formatDate(this.date));
13011                 this.hide();
13012                 break;
13013         }
13014     },
13015     
13016     
13017     onClick: function(e) {
13018         e.stopPropagation();
13019         e.preventDefault();
13020         
13021         var target = e.getTarget();
13022         
13023         if(target.nodeName.toLowerCase() === 'i'){
13024             target = Roo.get(target).dom.parentNode;
13025         }
13026         
13027         var nodeName = target.nodeName;
13028         var className = target.className;
13029         var html = target.innerHTML;
13030         
13031         switch(nodeName.toLowerCase()) {
13032             case 'th':
13033                 switch(className) {
13034                     case 'switch':
13035                         this.showMode(1);
13036                         break;
13037                     case 'prev':
13038                     case 'next':
13039                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13040                         switch(this.viewMode){
13041                                 case 0:
13042                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13043                                         break;
13044                                 case 1:
13045                                 case 2:
13046                                         this.viewDate = this.moveYear(this.viewDate, dir);
13047                                         break;
13048                         }
13049                         this.fill();
13050                         break;
13051                     case 'today':
13052                         var date = new Date();
13053                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13054                         this.fill()
13055                         this.setValue(this.formatDate(this.date));
13056                         this.hide();
13057                         break;
13058                 }
13059                 break;
13060             case 'span':
13061                 if (className.indexOf('disabled') === -1) {
13062                     this.viewDate.setUTCDate(1);
13063                     if (className.indexOf('month') !== -1) {
13064                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13065                     } else {
13066                         var year = parseInt(html, 10) || 0;
13067                         this.viewDate.setUTCFullYear(year);
13068                         
13069                     }
13070                     this.showMode(-1);
13071                     this.fill();
13072                 }
13073                 break;
13074                 
13075             case 'td':
13076                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13077                     var day = parseInt(html, 10) || 1;
13078                     var year = this.viewDate.getUTCFullYear(),
13079                         month = this.viewDate.getUTCMonth();
13080
13081                     if (className.indexOf('old') !== -1) {
13082                         if(month === 0 ){
13083                             month = 11;
13084                             year -= 1;
13085                         }else{
13086                             month -= 1;
13087                         }
13088                     } else if (className.indexOf('new') !== -1) {
13089                         if (month == 11) {
13090                             month = 0;
13091                             year += 1;
13092                         } else {
13093                             month += 1;
13094                         }
13095                     }
13096                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13097                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13098                     this.fill();
13099                     this.setValue(this.formatDate(this.date));
13100                     this.hide();
13101                 }
13102                 break;
13103         }
13104     },
13105     
13106     setStartDate: function(startDate){
13107         this.startDate = startDate || -Infinity;
13108         if (this.startDate !== -Infinity) {
13109             this.startDate = this.parseDate(this.startDate);
13110         }
13111         this.update();
13112         this.updateNavArrows();
13113     },
13114
13115     setEndDate: function(endDate){
13116         this.endDate = endDate || Infinity;
13117         if (this.endDate !== Infinity) {
13118             this.endDate = this.parseDate(this.endDate);
13119         }
13120         this.update();
13121         this.updateNavArrows();
13122     },
13123     
13124     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13125         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13126         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13127             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13128         }
13129         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13130             return parseInt(d, 10);
13131         });
13132         this.update();
13133         this.updateNavArrows();
13134     },
13135     
13136     updateNavArrows: function() {
13137         var d = new Date(this.viewDate),
13138         year = d.getUTCFullYear(),
13139         month = d.getUTCMonth();
13140         
13141         Roo.each(this.picker().select('.prev', true).elements, function(v){
13142             v.show();
13143             switch (this.viewMode) {
13144                 case 0:
13145
13146                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13147                         v.hide();
13148                     }
13149                     break;
13150                 case 1:
13151                 case 2:
13152                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13153                         v.hide();
13154                     }
13155                     break;
13156             }
13157         });
13158         
13159         Roo.each(this.picker().select('.next', true).elements, function(v){
13160             v.show();
13161             switch (this.viewMode) {
13162                 case 0:
13163
13164                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13165                         v.hide();
13166                     }
13167                     break;
13168                 case 1:
13169                 case 2:
13170                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13171                         v.hide();
13172                     }
13173                     break;
13174             }
13175         })
13176     },
13177     
13178     moveMonth: function(date, dir){
13179         if (!dir) return date;
13180         var new_date = new Date(date.valueOf()),
13181         day = new_date.getUTCDate(),
13182         month = new_date.getUTCMonth(),
13183         mag = Math.abs(dir),
13184         new_month, test;
13185         dir = dir > 0 ? 1 : -1;
13186         if (mag == 1){
13187             test = dir == -1
13188             // If going back one month, make sure month is not current month
13189             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13190             ? function(){
13191                 return new_date.getUTCMonth() == month;
13192             }
13193             // If going forward one month, make sure month is as expected
13194             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13195             : function(){
13196                 return new_date.getUTCMonth() != new_month;
13197             };
13198             new_month = month + dir;
13199             new_date.setUTCMonth(new_month);
13200             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13201             if (new_month < 0 || new_month > 11)
13202                 new_month = (new_month + 12) % 12;
13203         } else {
13204             // For magnitudes >1, move one month at a time...
13205             for (var i=0; i<mag; i++)
13206                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13207                 new_date = this.moveMonth(new_date, dir);
13208             // ...then reset the day, keeping it in the new month
13209             new_month = new_date.getUTCMonth();
13210             new_date.setUTCDate(day);
13211             test = function(){
13212                 return new_month != new_date.getUTCMonth();
13213             };
13214         }
13215         // Common date-resetting loop -- if date is beyond end of month, make it
13216         // end of month
13217         while (test()){
13218             new_date.setUTCDate(--day);
13219             new_date.setUTCMonth(new_month);
13220         }
13221         return new_date;
13222     },
13223
13224     moveYear: function(date, dir){
13225         return this.moveMonth(date, dir*12);
13226     },
13227
13228     dateWithinRange: function(date){
13229         return date >= this.startDate && date <= this.endDate;
13230     },
13231
13232     
13233     remove: function() {
13234         this.picker().remove();
13235     }
13236    
13237 });
13238
13239 Roo.apply(Roo.bootstrap.DateField,  {
13240     
13241     head : {
13242         tag: 'thead',
13243         cn: [
13244         {
13245             tag: 'tr',
13246             cn: [
13247             {
13248                 tag: 'th',
13249                 cls: 'prev',
13250                 html: '<i class="icon-arrow-left"/>'
13251             },
13252             {
13253                 tag: 'th',
13254                 cls: 'switch',
13255                 colspan: '5'
13256             },
13257             {
13258                 tag: 'th',
13259                 cls: 'next',
13260                 html: '<i class="icon-arrow-right"/>'
13261             }
13262
13263             ]
13264         }
13265         ]
13266     },
13267     
13268     content : {
13269         tag: 'tbody',
13270         cn: [
13271         {
13272             tag: 'tr',
13273             cn: [
13274             {
13275                 tag: 'td',
13276                 colspan: '7'
13277             }
13278             ]
13279         }
13280         ]
13281     },
13282     
13283     footer : {
13284         tag: 'tfoot',
13285         cn: [
13286         {
13287             tag: 'tr',
13288             cn: [
13289             {
13290                 tag: 'th',
13291                 colspan: '7',
13292                 cls: 'today'
13293             }
13294                     
13295             ]
13296         }
13297         ]
13298     },
13299     
13300     dates:{
13301         en: {
13302             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13303             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13304             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13305             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13306             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13307             today: "Today"
13308         }
13309     },
13310     
13311     modes: [
13312     {
13313         clsName: 'days',
13314         navFnc: 'Month',
13315         navStep: 1
13316     },
13317     {
13318         clsName: 'months',
13319         navFnc: 'FullYear',
13320         navStep: 1
13321     },
13322     {
13323         clsName: 'years',
13324         navFnc: 'FullYear',
13325         navStep: 10
13326     }]
13327 });
13328
13329 Roo.apply(Roo.bootstrap.DateField,  {
13330   
13331     template : {
13332         tag: 'div',
13333         cls: 'datepicker dropdown-menu',
13334         cn: [
13335         {
13336             tag: 'div',
13337             cls: 'datepicker-days',
13338             cn: [
13339             {
13340                 tag: 'table',
13341                 cls: 'table-condensed',
13342                 cn:[
13343                 Roo.bootstrap.DateField.head,
13344                 {
13345                     tag: 'tbody'
13346                 },
13347                 Roo.bootstrap.DateField.footer
13348                 ]
13349             }
13350             ]
13351         },
13352         {
13353             tag: 'div',
13354             cls: 'datepicker-months',
13355             cn: [
13356             {
13357                 tag: 'table',
13358                 cls: 'table-condensed',
13359                 cn:[
13360                 Roo.bootstrap.DateField.head,
13361                 Roo.bootstrap.DateField.content,
13362                 Roo.bootstrap.DateField.footer
13363                 ]
13364             }
13365             ]
13366         },
13367         {
13368             tag: 'div',
13369             cls: 'datepicker-years',
13370             cn: [
13371             {
13372                 tag: 'table',
13373                 cls: 'table-condensed',
13374                 cn:[
13375                 Roo.bootstrap.DateField.head,
13376                 Roo.bootstrap.DateField.content,
13377                 Roo.bootstrap.DateField.footer
13378                 ]
13379             }
13380             ]
13381         }
13382         ]
13383     }
13384 });
13385
13386  
13387
13388  /*
13389  * - LGPL
13390  *
13391  * TimeField
13392  * 
13393  */
13394
13395 /**
13396  * @class Roo.bootstrap.TimeField
13397  * @extends Roo.bootstrap.Input
13398  * Bootstrap DateField class
13399  * 
13400  * 
13401  * @constructor
13402  * Create a new TimeField
13403  * @param {Object} config The config object
13404  */
13405
13406 Roo.bootstrap.TimeField = function(config){
13407     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13408     this.addEvents({
13409             /**
13410              * @event show
13411              * Fires when this field show.
13412              * @param {Roo.bootstrap.DateField} this
13413              * @param {Mixed} date The date value
13414              */
13415             show : true,
13416             /**
13417              * @event show
13418              * Fires when this field hide.
13419              * @param {Roo.bootstrap.DateField} this
13420              * @param {Mixed} date The date value
13421              */
13422             hide : true,
13423             /**
13424              * @event select
13425              * Fires when select a date.
13426              * @param {Roo.bootstrap.DateField} this
13427              * @param {Mixed} date The date value
13428              */
13429             select : true
13430         });
13431 };
13432
13433 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13434     
13435     /**
13436      * @cfg {String} format
13437      * The default time format string which can be overriden for localization support.  The format must be
13438      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13439      */
13440     format : "H:i",
13441        
13442     onRender: function(ct, position)
13443     {
13444         
13445         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13446                 
13447         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13448         
13449         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13450         
13451         this.pop = this.picker().select('>.datepicker-time',true).first();
13452         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13453         
13454         this.picker().on('mousedown', this.onMousedown, this);
13455         this.picker().on('click', this.onClick, this);
13456         
13457         this.picker().addClass('datepicker-dropdown');
13458     
13459         this.fillTime();
13460         this.update();
13461             
13462         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13463         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13464         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13465         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13466         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13467         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13468
13469     },
13470     
13471     fireKey: function(e){
13472         if (!this.picker().isVisible()){
13473             if (e.keyCode == 27) // allow escape to hide and re-show picker
13474                 this.show();
13475             return;
13476         }
13477
13478         e.preventDefault();
13479         
13480         switch(e.keyCode){
13481             case 27: // escape
13482                 this.hide();
13483                 break;
13484             case 37: // left
13485             case 39: // right
13486                 this.onTogglePeriod();
13487                 break;
13488             case 38: // up
13489                 this.onIncrementMinutes();
13490                 break;
13491             case 40: // down
13492                 this.onDecrementMinutes();
13493                 break;
13494             case 13: // enter
13495             case 9: // tab
13496                 this.setTime();
13497                 break;
13498         }
13499     },
13500     
13501     onClick: function(e) {
13502         e.stopPropagation();
13503         e.preventDefault();
13504     },
13505     
13506     picker : function()
13507     {
13508         return this.el.select('.datepicker', true).first();
13509     },
13510     
13511     fillTime: function()
13512     {    
13513         var time = this.pop.select('tbody', true).first();
13514         
13515         time.dom.innerHTML = '';
13516         
13517         time.createChild({
13518             tag: 'tr',
13519             cn: [
13520                 {
13521                     tag: 'td',
13522                     cn: [
13523                         {
13524                             tag: 'a',
13525                             href: '#',
13526                             cls: 'btn',
13527                             cn: [
13528                                 {
13529                                     tag: 'span',
13530                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13531                                 }
13532                             ]
13533                         } 
13534                     ]
13535                 },
13536                 {
13537                     tag: 'td',
13538                     cls: 'separator'
13539                 },
13540                 {
13541                     tag: 'td',
13542                     cn: [
13543                         {
13544                             tag: 'a',
13545                             href: '#',
13546                             cls: 'btn',
13547                             cn: [
13548                                 {
13549                                     tag: 'span',
13550                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13551                                 }
13552                             ]
13553                         }
13554                     ]
13555                 },
13556                 {
13557                     tag: 'td',
13558                     cls: 'separator'
13559                 }
13560             ]
13561         });
13562         
13563         time.createChild({
13564             tag: 'tr',
13565             cn: [
13566                 {
13567                     tag: 'td',
13568                     cn: [
13569                         {
13570                             tag: 'span',
13571                             cls: 'timepicker-hour',
13572                             html: '00'
13573                         }  
13574                     ]
13575                 },
13576                 {
13577                     tag: 'td',
13578                     cls: 'separator',
13579                     html: ':'
13580                 },
13581                 {
13582                     tag: 'td',
13583                     cn: [
13584                         {
13585                             tag: 'span',
13586                             cls: 'timepicker-minute',
13587                             html: '00'
13588                         }  
13589                     ]
13590                 },
13591                 {
13592                     tag: 'td',
13593                     cls: 'separator'
13594                 },
13595                 {
13596                     tag: 'td',
13597                     cn: [
13598                         {
13599                             tag: 'button',
13600                             type: 'button',
13601                             cls: 'btn btn-primary period',
13602                             html: 'AM'
13603                             
13604                         }
13605                     ]
13606                 }
13607             ]
13608         });
13609         
13610         time.createChild({
13611             tag: 'tr',
13612             cn: [
13613                 {
13614                     tag: 'td',
13615                     cn: [
13616                         {
13617                             tag: 'a',
13618                             href: '#',
13619                             cls: 'btn',
13620                             cn: [
13621                                 {
13622                                     tag: 'span',
13623                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13624                                 }
13625                             ]
13626                         }
13627                     ]
13628                 },
13629                 {
13630                     tag: 'td',
13631                     cls: 'separator'
13632                 },
13633                 {
13634                     tag: 'td',
13635                     cn: [
13636                         {
13637                             tag: 'a',
13638                             href: '#',
13639                             cls: 'btn',
13640                             cn: [
13641                                 {
13642                                     tag: 'span',
13643                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13644                                 }
13645                             ]
13646                         }
13647                     ]
13648                 },
13649                 {
13650                     tag: 'td',
13651                     cls: 'separator'
13652                 }
13653             ]
13654         });
13655         
13656     },
13657     
13658     update: function()
13659     {
13660         
13661         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13662         
13663         this.fill();
13664     },
13665     
13666     fill: function() 
13667     {
13668         var hours = this.time.getHours();
13669         var minutes = this.time.getMinutes();
13670         var period = 'AM';
13671         
13672         if(hours > 11){
13673             period = 'PM';
13674         }
13675         
13676         if(hours == 0){
13677             hours = 12;
13678         }
13679         
13680         
13681         if(hours > 12){
13682             hours = hours - 12;
13683         }
13684         
13685         if(hours < 10){
13686             hours = '0' + hours;
13687         }
13688         
13689         if(minutes < 10){
13690             minutes = '0' + minutes;
13691         }
13692         
13693         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13694         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13695         this.pop.select('button', true).first().dom.innerHTML = period;
13696         
13697     },
13698     
13699     place: function()
13700     {   
13701         this.picker().removeClass(['bottom', 'top']);
13702         
13703         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13704             /*
13705              * place to the top of element!
13706              *
13707              */
13708             
13709             this.picker().addClass('top');
13710             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13711             
13712             return;
13713         }
13714         
13715         this.picker().addClass('bottom');
13716         
13717         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13718     },
13719   
13720     onFocus : function()
13721     {
13722         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13723         this.show();
13724     },
13725     
13726     onBlur : function()
13727     {
13728         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13729         this.hide();
13730     },
13731     
13732     show : function()
13733     {
13734         this.picker().show();
13735         this.pop.show();
13736         this.update();
13737         this.place();
13738         
13739         this.fireEvent('show', this, this.date);
13740     },
13741     
13742     hide : function()
13743     {
13744         this.picker().hide();
13745         this.pop.hide();
13746         
13747         this.fireEvent('hide', this, this.date);
13748     },
13749     
13750     setTime : function()
13751     {
13752         this.hide();
13753         this.setValue(this.time.format(this.format));
13754         
13755         this.fireEvent('select', this, this.date);
13756         
13757         
13758     },
13759     
13760     onMousedown: function(e){
13761         e.stopPropagation();
13762         e.preventDefault();
13763     },
13764     
13765     onIncrementHours: function()
13766     {
13767         Roo.log('onIncrementHours');
13768         this.time = this.time.add(Date.HOUR, 1);
13769         this.update();
13770         
13771     },
13772     
13773     onDecrementHours: function()
13774     {
13775         Roo.log('onDecrementHours');
13776         this.time = this.time.add(Date.HOUR, -1);
13777         this.update();
13778     },
13779     
13780     onIncrementMinutes: function()
13781     {
13782         Roo.log('onIncrementMinutes');
13783         this.time = this.time.add(Date.MINUTE, 1);
13784         this.update();
13785     },
13786     
13787     onDecrementMinutes: function()
13788     {
13789         Roo.log('onDecrementMinutes');
13790         this.time = this.time.add(Date.MINUTE, -1);
13791         this.update();
13792     },
13793     
13794     onTogglePeriod: function()
13795     {
13796         Roo.log('onTogglePeriod');
13797         this.time = this.time.add(Date.HOUR, 12);
13798         this.update();
13799     }
13800     
13801    
13802 });
13803
13804 Roo.apply(Roo.bootstrap.TimeField,  {
13805     
13806     content : {
13807         tag: 'tbody',
13808         cn: [
13809             {
13810                 tag: 'tr',
13811                 cn: [
13812                 {
13813                     tag: 'td',
13814                     colspan: '7'
13815                 }
13816                 ]
13817             }
13818         ]
13819     },
13820     
13821     footer : {
13822         tag: 'tfoot',
13823         cn: [
13824             {
13825                 tag: 'tr',
13826                 cn: [
13827                 {
13828                     tag: 'th',
13829                     colspan: '7',
13830                     cls: '',
13831                     cn: [
13832                         {
13833                             tag: 'button',
13834                             cls: 'btn btn-info ok',
13835                             html: 'OK'
13836                         }
13837                     ]
13838                 }
13839
13840                 ]
13841             }
13842         ]
13843     }
13844 });
13845
13846 Roo.apply(Roo.bootstrap.TimeField,  {
13847   
13848     template : {
13849         tag: 'div',
13850         cls: 'datepicker dropdown-menu',
13851         cn: [
13852             {
13853                 tag: 'div',
13854                 cls: 'datepicker-time',
13855                 cn: [
13856                 {
13857                     tag: 'table',
13858                     cls: 'table-condensed',
13859                     cn:[
13860                     Roo.bootstrap.TimeField.content,
13861                     Roo.bootstrap.TimeField.footer
13862                     ]
13863                 }
13864                 ]
13865             }
13866         ]
13867     }
13868 });
13869
13870  
13871
13872  /*
13873  * - LGPL
13874  *
13875  * CheckBox
13876  * 
13877  */
13878
13879 /**
13880  * @class Roo.bootstrap.CheckBox
13881  * @extends Roo.bootstrap.Input
13882  * Bootstrap CheckBox class
13883  * 
13884  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13885  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13886  * @cfg {String} boxLabel The text that appears beside the checkbox
13887  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
13888  * @cfg {Boolean} checked initnal the element
13889  * 
13890  * 
13891  * @constructor
13892  * Create a new CheckBox
13893  * @param {Object} config The config object
13894  */
13895
13896 Roo.bootstrap.CheckBox = function(config){
13897     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13898    
13899         this.addEvents({
13900             /**
13901             * @event check
13902             * Fires when the element is checked or unchecked.
13903             * @param {Roo.bootstrap.CheckBox} this This input
13904             * @param {Boolean} checked The new checked value
13905             */
13906            check : true
13907         });
13908 };
13909
13910 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13911     
13912     inputType: 'checkbox',
13913     inputValue: 1,
13914     valueOff: 0,
13915     boxLabel: false,
13916     checked: false,
13917     weight : false,
13918     
13919     getAutoCreate : function()
13920     {
13921         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13922         
13923         var id = Roo.id();
13924         
13925         var cfg = {};
13926         
13927         cfg.cls = 'form-group checkbox' //input-group
13928         
13929         
13930         
13931         
13932         var input =  {
13933             tag: 'input',
13934             id : id,
13935             type : this.inputType,
13936             value : (!this.checked) ? this.valueOff : this.inputValue,
13937             cls : 'roo-checkbox', //'form-box',
13938             placeholder : this.placeholder || ''
13939             
13940         };
13941         
13942         if (this.weight) { // Validity check?
13943             cfg.cls += " checkbox-" + this.weight;
13944         }
13945         
13946         if (this.disabled) {
13947             input.disabled=true;
13948         }
13949         
13950         if(this.checked){
13951             input.checked = this.checked;
13952         }
13953         
13954         if (this.name) {
13955             input.name = this.name;
13956         }
13957         
13958         if (this.size) {
13959             input.cls += ' input-' + this.size;
13960         }
13961         
13962         var settings=this;
13963         ['xs','sm','md','lg'].map(function(size){
13964             if (settings[size]) {
13965                 cfg.cls += ' col-' + size + '-' + settings[size];
13966             }
13967         });
13968         
13969        
13970         
13971         var inputblock = input;
13972         
13973         
13974         
13975         
13976         if (this.before || this.after) {
13977             
13978             inputblock = {
13979                 cls : 'input-group',
13980                 cn :  [] 
13981             };
13982             if (this.before) {
13983                 inputblock.cn.push({
13984                     tag :'span',
13985                     cls : 'input-group-addon',
13986                     html : this.before
13987                 });
13988             }
13989             inputblock.cn.push(input);
13990             if (this.after) {
13991                 inputblock.cn.push({
13992                     tag :'span',
13993                     cls : 'input-group-addon',
13994                     html : this.after
13995                 });
13996             }
13997             
13998         };
13999         
14000         if (align ==='left' && this.fieldLabel.length) {
14001                 Roo.log("left and has label");
14002                 cfg.cn = [
14003                     
14004                     {
14005                         tag: 'label',
14006                         'for' :  id,
14007                         cls : 'control-label col-md-' + this.labelWidth,
14008                         html : this.fieldLabel
14009                         
14010                     },
14011                     {
14012                         cls : "col-md-" + (12 - this.labelWidth), 
14013                         cn: [
14014                             inputblock
14015                         ]
14016                     }
14017                     
14018                 ];
14019         } else if ( this.fieldLabel.length) {
14020                 Roo.log(" label");
14021                 cfg.cn = [
14022                    
14023                     {
14024                         tag: this.boxLabel ? 'span' : 'label',
14025                         'for': id,
14026                         cls: 'control-label box-input-label',
14027                         //cls : 'input-group-addon',
14028                         html : this.fieldLabel
14029                         
14030                     },
14031                     
14032                     inputblock
14033                     
14034                 ];
14035
14036         } else {
14037             
14038                 Roo.log(" no label && no align");
14039                 cfg.cn = [  inputblock ] ;
14040                 
14041                 
14042         };
14043          if(this.boxLabel){
14044             cfg.cn.push( {
14045                 tag: 'label',
14046                 'for': id,
14047                 cls: 'box-label',
14048                 html: this.boxLabel
14049                 
14050             });
14051         }
14052         
14053         
14054        
14055         return cfg;
14056         
14057     },
14058     
14059     /**
14060      * return the real input element.
14061      */
14062     inputEl: function ()
14063     {
14064         return this.el.select('input.roo-checkbox',true).first();
14065     },
14066     
14067     label: function()
14068     {
14069         return this.el.select('label.control-label',true).first();
14070     },
14071     
14072     initEvents : function()
14073     {
14074 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14075         
14076         this.inputEl().on('click', this.onClick,  this);
14077         
14078     },
14079     
14080     onClick : function()
14081     {   
14082         this.setChecked(!this.checked);
14083     },
14084     
14085     setChecked : function(state,suppressEvent)
14086     {
14087         this.checked = state;
14088         
14089         this.inputEl().dom.checked = state;
14090         
14091         if(suppressEvent !== true){
14092             this.fireEvent('check', this, state);
14093         }
14094         
14095         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14096         
14097     },
14098     
14099     setValue : function(v,suppressEvent)
14100     {
14101         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14102     }
14103     
14104 });
14105
14106  
14107 /*
14108  * - LGPL
14109  *
14110  * Radio
14111  * 
14112  */
14113
14114 /**
14115  * @class Roo.bootstrap.Radio
14116  * @extends Roo.bootstrap.CheckBox
14117  * Bootstrap Radio class
14118
14119  * @constructor
14120  * Create a new Radio
14121  * @param {Object} config The config object
14122  */
14123
14124 Roo.bootstrap.Radio = function(config){
14125     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14126    
14127 };
14128
14129 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14130     
14131     inputType: 'radio',
14132     inputValue: '',
14133     valueOff: '',
14134     
14135     getAutoCreate : function()
14136     {
14137         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14138         
14139         var id = Roo.id();
14140         
14141         var cfg = {};
14142         
14143         cfg.cls = 'form-group' //input-group
14144         
14145         var input =  {
14146             tag: 'input',
14147             id : id,
14148             type : this.inputType,
14149             value : (!this.checked) ? this.valueOff : this.inputValue,
14150             cls : 'form-box',
14151             placeholder : this.placeholder || ''
14152             
14153         };
14154         
14155         if (this.disabled) {
14156             input.disabled=true;
14157         }
14158         
14159         if(this.checked){
14160             input.checked = this.checked;
14161         }
14162         
14163         if (this.name) {
14164             input.name = this.name;
14165         }
14166         
14167         if (this.size) {
14168             input.cls += ' input-' + this.size;
14169         }
14170         
14171         var settings=this;
14172         ['xs','sm','md','lg'].map(function(size){
14173             if (settings[size]) {
14174                 cfg.cls += ' col-' + size + '-' + settings[size];
14175             }
14176         });
14177         
14178         var inputblock = input;
14179         
14180         if (this.before || this.after) {
14181             
14182             inputblock = {
14183                 cls : 'input-group',
14184                 cn :  [] 
14185             };
14186             if (this.before) {
14187                 inputblock.cn.push({
14188                     tag :'span',
14189                     cls : 'input-group-addon',
14190                     html : this.before
14191                 });
14192             }
14193             inputblock.cn.push(input);
14194             if (this.after) {
14195                 inputblock.cn.push({
14196                     tag :'span',
14197                     cls : 'input-group-addon',
14198                     html : this.after
14199                 });
14200             }
14201             
14202         };
14203         
14204         if (align ==='left' && this.fieldLabel.length) {
14205                 Roo.log("left and has label");
14206                 cfg.cn = [
14207                     
14208                     {
14209                         tag: 'label',
14210                         'for' :  id,
14211                         cls : 'control-label col-md-' + this.labelWidth,
14212                         html : this.fieldLabel
14213                         
14214                     },
14215                     {
14216                         cls : "col-md-" + (12 - this.labelWidth), 
14217                         cn: [
14218                             inputblock
14219                         ]
14220                     }
14221                     
14222                 ];
14223         } else if ( this.fieldLabel.length) {
14224                 Roo.log(" label");
14225                  cfg.cn = [
14226                    
14227                     {
14228                         tag: 'label',
14229                         'for': id,
14230                         cls: 'control-label box-input-label',
14231                         //cls : 'input-group-addon',
14232                         html : this.fieldLabel
14233                         
14234                     },
14235                     
14236                     inputblock
14237                     
14238                 ];
14239
14240         } else {
14241             
14242                    Roo.log(" no label && no align");
14243                 cfg.cn = [
14244                     
14245                         inputblock
14246                     
14247                 ];
14248                 
14249                 
14250         };
14251         
14252         if(this.boxLabel){
14253             cfg.cn.push({
14254                 tag: 'label',
14255                 'for': id,
14256                 cls: 'box-label',
14257                 html: this.boxLabel
14258             })
14259         }
14260         
14261         return cfg;
14262         
14263     },
14264    
14265     onClick : function()
14266     {   
14267         this.setChecked(true);
14268     },
14269     
14270     setChecked : function(state,suppressEvent)
14271     {
14272         if(state){
14273             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14274                 v.dom.checked = false;
14275             });
14276         }
14277         
14278         this.checked = state;
14279         this.inputEl().dom.checked = state;
14280         
14281         if(suppressEvent !== true){
14282             this.fireEvent('check', this, state);
14283         }
14284         
14285         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14286         
14287     },
14288     
14289     getGroupValue : function()
14290     {
14291         var value = ''
14292         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14293             if(v.dom.checked == true){
14294                 value = v.dom.value;
14295             }
14296         });
14297         
14298         return value;
14299     },
14300     
14301     /**
14302      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14303      * @return {Mixed} value The field value
14304      */
14305     getValue : function(){
14306         return this.getGroupValue();
14307     }
14308     
14309 });
14310
14311  
14312 //<script type="text/javascript">
14313
14314 /*
14315  * Based  Ext JS Library 1.1.1
14316  * Copyright(c) 2006-2007, Ext JS, LLC.
14317  * LGPL
14318  *
14319  */
14320  
14321 /**
14322  * @class Roo.HtmlEditorCore
14323  * @extends Roo.Component
14324  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14325  *
14326  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14327  */
14328
14329 Roo.HtmlEditorCore = function(config){
14330     
14331     
14332     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14333     this.addEvents({
14334         /**
14335          * @event initialize
14336          * Fires when the editor is fully initialized (including the iframe)
14337          * @param {Roo.HtmlEditorCore} this
14338          */
14339         initialize: true,
14340         /**
14341          * @event activate
14342          * Fires when the editor is first receives the focus. Any insertion must wait
14343          * until after this event.
14344          * @param {Roo.HtmlEditorCore} this
14345          */
14346         activate: true,
14347          /**
14348          * @event beforesync
14349          * Fires before the textarea is updated with content from the editor iframe. Return false
14350          * to cancel the sync.
14351          * @param {Roo.HtmlEditorCore} this
14352          * @param {String} html
14353          */
14354         beforesync: true,
14355          /**
14356          * @event beforepush
14357          * Fires before the iframe editor is updated with content from the textarea. Return false
14358          * to cancel the push.
14359          * @param {Roo.HtmlEditorCore} this
14360          * @param {String} html
14361          */
14362         beforepush: true,
14363          /**
14364          * @event sync
14365          * Fires when the textarea is updated with content from the editor iframe.
14366          * @param {Roo.HtmlEditorCore} this
14367          * @param {String} html
14368          */
14369         sync: true,
14370          /**
14371          * @event push
14372          * Fires when the iframe editor is updated with content from the textarea.
14373          * @param {Roo.HtmlEditorCore} this
14374          * @param {String} html
14375          */
14376         push: true,
14377         
14378         /**
14379          * @event editorevent
14380          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14381          * @param {Roo.HtmlEditorCore} this
14382          */
14383         editorevent: true
14384     });
14385      
14386 };
14387
14388
14389 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14390
14391
14392      /**
14393      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14394      */
14395     
14396     owner : false,
14397     
14398      /**
14399      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14400      *                        Roo.resizable.
14401      */
14402     resizable : false,
14403      /**
14404      * @cfg {Number} height (in pixels)
14405      */   
14406     height: 300,
14407    /**
14408      * @cfg {Number} width (in pixels)
14409      */   
14410     width: 500,
14411     
14412     /**
14413      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14414      * 
14415      */
14416     stylesheets: false,
14417     
14418     // id of frame..
14419     frameId: false,
14420     
14421     // private properties
14422     validationEvent : false,
14423     deferHeight: true,
14424     initialized : false,
14425     activated : false,
14426     sourceEditMode : false,
14427     onFocus : Roo.emptyFn,
14428     iframePad:3,
14429     hideMode:'offsets',
14430     
14431     clearUp: true,
14432     
14433      
14434     
14435
14436     /**
14437      * Protected method that will not generally be called directly. It
14438      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14439      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14440      */
14441     getDocMarkup : function(){
14442         // body styles..
14443         var st = '';
14444         Roo.log(this.stylesheets);
14445         
14446         // inherit styels from page...?? 
14447         if (this.stylesheets === false) {
14448             
14449             Roo.get(document.head).select('style').each(function(node) {
14450                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14451             });
14452             
14453             Roo.get(document.head).select('link').each(function(node) { 
14454                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14455             });
14456             
14457         } else if (!this.stylesheets.length) {
14458                 // simple..
14459                 st = '<style type="text/css">' +
14460                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14461                    '</style>';
14462         } else {
14463             Roo.each(this.stylesheets, function(s) {
14464                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14465             });
14466             
14467         }
14468         
14469         st +=  '<style type="text/css">' +
14470             'IMG { cursor: pointer } ' +
14471         '</style>';
14472
14473         
14474         return '<html><head>' + st  +
14475             //<style type="text/css">' +
14476             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14477             //'</style>' +
14478             ' </head><body class="roo-htmleditor-body"></body></html>';
14479     },
14480
14481     // private
14482     onRender : function(ct, position)
14483     {
14484         var _t = this;
14485         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14486         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14487         
14488         
14489         this.el.dom.style.border = '0 none';
14490         this.el.dom.setAttribute('tabIndex', -1);
14491         this.el.addClass('x-hidden hide');
14492         
14493         
14494         
14495         if(Roo.isIE){ // fix IE 1px bogus margin
14496             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14497         }
14498        
14499         
14500         this.frameId = Roo.id();
14501         
14502          
14503         
14504         var iframe = this.owner.wrap.createChild({
14505             tag: 'iframe',
14506             cls: 'form-control', // bootstrap..
14507             id: this.frameId,
14508             name: this.frameId,
14509             frameBorder : 'no',
14510             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14511         }, this.el
14512         );
14513         
14514         
14515         this.iframe = iframe.dom;
14516
14517          this.assignDocWin();
14518         
14519         this.doc.designMode = 'on';
14520        
14521         this.doc.open();
14522         this.doc.write(this.getDocMarkup());
14523         this.doc.close();
14524
14525         
14526         var task = { // must defer to wait for browser to be ready
14527             run : function(){
14528                 //console.log("run task?" + this.doc.readyState);
14529                 this.assignDocWin();
14530                 if(this.doc.body || this.doc.readyState == 'complete'){
14531                     try {
14532                         this.doc.designMode="on";
14533                     } catch (e) {
14534                         return;
14535                     }
14536                     Roo.TaskMgr.stop(task);
14537                     this.initEditor.defer(10, this);
14538                 }
14539             },
14540             interval : 10,
14541             duration: 10000,
14542             scope: this
14543         };
14544         Roo.TaskMgr.start(task);
14545
14546         
14547          
14548     },
14549
14550     // private
14551     onResize : function(w, h)
14552     {
14553          Roo.log('resize: ' +w + ',' + h );
14554         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14555         if(!this.iframe){
14556             return;
14557         }
14558         if(typeof w == 'number'){
14559             
14560             this.iframe.style.width = w + 'px';
14561         }
14562         if(typeof h == 'number'){
14563             
14564             this.iframe.style.height = h + 'px';
14565             if(this.doc){
14566                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14567             }
14568         }
14569         
14570     },
14571
14572     /**
14573      * Toggles the editor between standard and source edit mode.
14574      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14575      */
14576     toggleSourceEdit : function(sourceEditMode){
14577         
14578         this.sourceEditMode = sourceEditMode === true;
14579         
14580         if(this.sourceEditMode){
14581  
14582             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14583             
14584         }else{
14585             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14586             //this.iframe.className = '';
14587             this.deferFocus();
14588         }
14589         //this.setSize(this.owner.wrap.getSize());
14590         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14591     },
14592
14593     
14594   
14595
14596     /**
14597      * Protected method that will not generally be called directly. If you need/want
14598      * custom HTML cleanup, this is the method you should override.
14599      * @param {String} html The HTML to be cleaned
14600      * return {String} The cleaned HTML
14601      */
14602     cleanHtml : function(html){
14603         html = String(html);
14604         if(html.length > 5){
14605             if(Roo.isSafari){ // strip safari nonsense
14606                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14607             }
14608         }
14609         if(html == '&nbsp;'){
14610             html = '';
14611         }
14612         return html;
14613     },
14614
14615     /**
14616      * HTML Editor -> Textarea
14617      * Protected method that will not generally be called directly. Syncs the contents
14618      * of the editor iframe with the textarea.
14619      */
14620     syncValue : function(){
14621         if(this.initialized){
14622             var bd = (this.doc.body || this.doc.documentElement);
14623             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14624             var html = bd.innerHTML;
14625             if(Roo.isSafari){
14626                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14627                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14628                 if(m && m[1]){
14629                     html = '<div style="'+m[0]+'">' + html + '</div>';
14630                 }
14631             }
14632             html = this.cleanHtml(html);
14633             // fix up the special chars.. normaly like back quotes in word...
14634             // however we do not want to do this with chinese..
14635             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14636                 var cc = b.charCodeAt();
14637                 if (
14638                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14639                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14640                     (cc >= 0xf900 && cc < 0xfb00 )
14641                 ) {
14642                         return b;
14643                 }
14644                 return "&#"+cc+";" 
14645             });
14646             if(this.owner.fireEvent('beforesync', this, html) !== false){
14647                 this.el.dom.value = html;
14648                 this.owner.fireEvent('sync', this, html);
14649             }
14650         }
14651     },
14652
14653     /**
14654      * Protected method that will not generally be called directly. Pushes the value of the textarea
14655      * into the iframe editor.
14656      */
14657     pushValue : function(){
14658         if(this.initialized){
14659             var v = this.el.dom.value.trim();
14660             
14661 //            if(v.length < 1){
14662 //                v = '&#160;';
14663 //            }
14664             
14665             if(this.owner.fireEvent('beforepush', this, v) !== false){
14666                 var d = (this.doc.body || this.doc.documentElement);
14667                 d.innerHTML = v;
14668                 this.cleanUpPaste();
14669                 this.el.dom.value = d.innerHTML;
14670                 this.owner.fireEvent('push', this, v);
14671             }
14672         }
14673     },
14674
14675     // private
14676     deferFocus : function(){
14677         this.focus.defer(10, this);
14678     },
14679
14680     // doc'ed in Field
14681     focus : function(){
14682         if(this.win && !this.sourceEditMode){
14683             this.win.focus();
14684         }else{
14685             this.el.focus();
14686         }
14687     },
14688     
14689     assignDocWin: function()
14690     {
14691         var iframe = this.iframe;
14692         
14693          if(Roo.isIE){
14694             this.doc = iframe.contentWindow.document;
14695             this.win = iframe.contentWindow;
14696         } else {
14697             if (!Roo.get(this.frameId)) {
14698                 return;
14699             }
14700             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14701             this.win = Roo.get(this.frameId).dom.contentWindow;
14702         }
14703     },
14704     
14705     // private
14706     initEditor : function(){
14707         //console.log("INIT EDITOR");
14708         this.assignDocWin();
14709         
14710         
14711         
14712         this.doc.designMode="on";
14713         this.doc.open();
14714         this.doc.write(this.getDocMarkup());
14715         this.doc.close();
14716         
14717         var dbody = (this.doc.body || this.doc.documentElement);
14718         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14719         // this copies styles from the containing element into thsi one..
14720         // not sure why we need all of this..
14721         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14722         ss['background-attachment'] = 'fixed'; // w3c
14723         dbody.bgProperties = 'fixed'; // ie
14724         Roo.DomHelper.applyStyles(dbody, ss);
14725         Roo.EventManager.on(this.doc, {
14726             //'mousedown': this.onEditorEvent,
14727             'mouseup': this.onEditorEvent,
14728             'dblclick': this.onEditorEvent,
14729             'click': this.onEditorEvent,
14730             'keyup': this.onEditorEvent,
14731             buffer:100,
14732             scope: this
14733         });
14734         if(Roo.isGecko){
14735             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14736         }
14737         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14738             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14739         }
14740         this.initialized = true;
14741
14742         this.owner.fireEvent('initialize', this);
14743         this.pushValue();
14744     },
14745
14746     // private
14747     onDestroy : function(){
14748         
14749         
14750         
14751         if(this.rendered){
14752             
14753             //for (var i =0; i < this.toolbars.length;i++) {
14754             //    // fixme - ask toolbars for heights?
14755             //    this.toolbars[i].onDestroy();
14756            // }
14757             
14758             //this.wrap.dom.innerHTML = '';
14759             //this.wrap.remove();
14760         }
14761     },
14762
14763     // private
14764     onFirstFocus : function(){
14765         
14766         this.assignDocWin();
14767         
14768         
14769         this.activated = true;
14770          
14771     
14772         if(Roo.isGecko){ // prevent silly gecko errors
14773             this.win.focus();
14774             var s = this.win.getSelection();
14775             if(!s.focusNode || s.focusNode.nodeType != 3){
14776                 var r = s.getRangeAt(0);
14777                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14778                 r.collapse(true);
14779                 this.deferFocus();
14780             }
14781             try{
14782                 this.execCmd('useCSS', true);
14783                 this.execCmd('styleWithCSS', false);
14784             }catch(e){}
14785         }
14786         this.owner.fireEvent('activate', this);
14787     },
14788
14789     // private
14790     adjustFont: function(btn){
14791         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14792         //if(Roo.isSafari){ // safari
14793         //    adjust *= 2;
14794        // }
14795         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14796         if(Roo.isSafari){ // safari
14797             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14798             v =  (v < 10) ? 10 : v;
14799             v =  (v > 48) ? 48 : v;
14800             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14801             
14802         }
14803         
14804         
14805         v = Math.max(1, v+adjust);
14806         
14807         this.execCmd('FontSize', v  );
14808     },
14809
14810     onEditorEvent : function(e){
14811         this.owner.fireEvent('editorevent', this, e);
14812       //  this.updateToolbar();
14813         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14814     },
14815
14816     insertTag : function(tg)
14817     {
14818         // could be a bit smarter... -> wrap the current selected tRoo..
14819         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14820             
14821             range = this.createRange(this.getSelection());
14822             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14823             wrappingNode.appendChild(range.extractContents());
14824             range.insertNode(wrappingNode);
14825
14826             return;
14827             
14828             
14829             
14830         }
14831         this.execCmd("formatblock",   tg);
14832         
14833     },
14834     
14835     insertText : function(txt)
14836     {
14837         
14838         
14839         var range = this.createRange();
14840         range.deleteContents();
14841                //alert(Sender.getAttribute('label'));
14842                
14843         range.insertNode(this.doc.createTextNode(txt));
14844     } ,
14845     
14846      
14847
14848     /**
14849      * Executes a Midas editor command on the editor document and performs necessary focus and
14850      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14851      * @param {String} cmd The Midas command
14852      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14853      */
14854     relayCmd : function(cmd, value){
14855         this.win.focus();
14856         this.execCmd(cmd, value);
14857         this.owner.fireEvent('editorevent', this);
14858         //this.updateToolbar();
14859         this.owner.deferFocus();
14860     },
14861
14862     /**
14863      * Executes a Midas editor command directly on the editor document.
14864      * For visual commands, you should use {@link #relayCmd} instead.
14865      * <b>This should only be called after the editor is initialized.</b>
14866      * @param {String} cmd The Midas command
14867      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14868      */
14869     execCmd : function(cmd, value){
14870         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14871         this.syncValue();
14872     },
14873  
14874  
14875    
14876     /**
14877      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14878      * to insert tRoo.
14879      * @param {String} text | dom node.. 
14880      */
14881     insertAtCursor : function(text)
14882     {
14883         
14884         
14885         
14886         if(!this.activated){
14887             return;
14888         }
14889         /*
14890         if(Roo.isIE){
14891             this.win.focus();
14892             var r = this.doc.selection.createRange();
14893             if(r){
14894                 r.collapse(true);
14895                 r.pasteHTML(text);
14896                 this.syncValue();
14897                 this.deferFocus();
14898             
14899             }
14900             return;
14901         }
14902         */
14903         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14904             this.win.focus();
14905             
14906             
14907             // from jquery ui (MIT licenced)
14908             var range, node;
14909             var win = this.win;
14910             
14911             if (win.getSelection && win.getSelection().getRangeAt) {
14912                 range = win.getSelection().getRangeAt(0);
14913                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14914                 range.insertNode(node);
14915             } else if (win.document.selection && win.document.selection.createRange) {
14916                 // no firefox support
14917                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14918                 win.document.selection.createRange().pasteHTML(txt);
14919             } else {
14920                 // no firefox support
14921                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14922                 this.execCmd('InsertHTML', txt);
14923             } 
14924             
14925             this.syncValue();
14926             
14927             this.deferFocus();
14928         }
14929     },
14930  // private
14931     mozKeyPress : function(e){
14932         if(e.ctrlKey){
14933             var c = e.getCharCode(), cmd;
14934           
14935             if(c > 0){
14936                 c = String.fromCharCode(c).toLowerCase();
14937                 switch(c){
14938                     case 'b':
14939                         cmd = 'bold';
14940                         break;
14941                     case 'i':
14942                         cmd = 'italic';
14943                         break;
14944                     
14945                     case 'u':
14946                         cmd = 'underline';
14947                         break;
14948                     
14949                     case 'v':
14950                         this.cleanUpPaste.defer(100, this);
14951                         return;
14952                         
14953                 }
14954                 if(cmd){
14955                     this.win.focus();
14956                     this.execCmd(cmd);
14957                     this.deferFocus();
14958                     e.preventDefault();
14959                 }
14960                 
14961             }
14962         }
14963     },
14964
14965     // private
14966     fixKeys : function(){ // load time branching for fastest keydown performance
14967         if(Roo.isIE){
14968             return function(e){
14969                 var k = e.getKey(), r;
14970                 if(k == e.TAB){
14971                     e.stopEvent();
14972                     r = this.doc.selection.createRange();
14973                     if(r){
14974                         r.collapse(true);
14975                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14976                         this.deferFocus();
14977                     }
14978                     return;
14979                 }
14980                 
14981                 if(k == e.ENTER){
14982                     r = this.doc.selection.createRange();
14983                     if(r){
14984                         var target = r.parentElement();
14985                         if(!target || target.tagName.toLowerCase() != 'li'){
14986                             e.stopEvent();
14987                             r.pasteHTML('<br />');
14988                             r.collapse(false);
14989                             r.select();
14990                         }
14991                     }
14992                 }
14993                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14994                     this.cleanUpPaste.defer(100, this);
14995                     return;
14996                 }
14997                 
14998                 
14999             };
15000         }else if(Roo.isOpera){
15001             return function(e){
15002                 var k = e.getKey();
15003                 if(k == e.TAB){
15004                     e.stopEvent();
15005                     this.win.focus();
15006                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15007                     this.deferFocus();
15008                 }
15009                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15010                     this.cleanUpPaste.defer(100, this);
15011                     return;
15012                 }
15013                 
15014             };
15015         }else if(Roo.isSafari){
15016             return function(e){
15017                 var k = e.getKey();
15018                 
15019                 if(k == e.TAB){
15020                     e.stopEvent();
15021                     this.execCmd('InsertText','\t');
15022                     this.deferFocus();
15023                     return;
15024                 }
15025                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15026                     this.cleanUpPaste.defer(100, this);
15027                     return;
15028                 }
15029                 
15030              };
15031         }
15032     }(),
15033     
15034     getAllAncestors: function()
15035     {
15036         var p = this.getSelectedNode();
15037         var a = [];
15038         if (!p) {
15039             a.push(p); // push blank onto stack..
15040             p = this.getParentElement();
15041         }
15042         
15043         
15044         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15045             a.push(p);
15046             p = p.parentNode;
15047         }
15048         a.push(this.doc.body);
15049         return a;
15050     },
15051     lastSel : false,
15052     lastSelNode : false,
15053     
15054     
15055     getSelection : function() 
15056     {
15057         this.assignDocWin();
15058         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15059     },
15060     
15061     getSelectedNode: function() 
15062     {
15063         // this may only work on Gecko!!!
15064         
15065         // should we cache this!!!!
15066         
15067         
15068         
15069          
15070         var range = this.createRange(this.getSelection()).cloneRange();
15071         
15072         if (Roo.isIE) {
15073             var parent = range.parentElement();
15074             while (true) {
15075                 var testRange = range.duplicate();
15076                 testRange.moveToElementText(parent);
15077                 if (testRange.inRange(range)) {
15078                     break;
15079                 }
15080                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15081                     break;
15082                 }
15083                 parent = parent.parentElement;
15084             }
15085             return parent;
15086         }
15087         
15088         // is ancestor a text element.
15089         var ac =  range.commonAncestorContainer;
15090         if (ac.nodeType == 3) {
15091             ac = ac.parentNode;
15092         }
15093         
15094         var ar = ac.childNodes;
15095          
15096         var nodes = [];
15097         var other_nodes = [];
15098         var has_other_nodes = false;
15099         for (var i=0;i<ar.length;i++) {
15100             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15101                 continue;
15102             }
15103             // fullly contained node.
15104             
15105             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15106                 nodes.push(ar[i]);
15107                 continue;
15108             }
15109             
15110             // probably selected..
15111             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15112                 other_nodes.push(ar[i]);
15113                 continue;
15114             }
15115             // outer..
15116             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15117                 continue;
15118             }
15119             
15120             
15121             has_other_nodes = true;
15122         }
15123         if (!nodes.length && other_nodes.length) {
15124             nodes= other_nodes;
15125         }
15126         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15127             return false;
15128         }
15129         
15130         return nodes[0];
15131     },
15132     createRange: function(sel)
15133     {
15134         // this has strange effects when using with 
15135         // top toolbar - not sure if it's a great idea.
15136         //this.editor.contentWindow.focus();
15137         if (typeof sel != "undefined") {
15138             try {
15139                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15140             } catch(e) {
15141                 return this.doc.createRange();
15142             }
15143         } else {
15144             return this.doc.createRange();
15145         }
15146     },
15147     getParentElement: function()
15148     {
15149         
15150         this.assignDocWin();
15151         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15152         
15153         var range = this.createRange(sel);
15154          
15155         try {
15156             var p = range.commonAncestorContainer;
15157             while (p.nodeType == 3) { // text node
15158                 p = p.parentNode;
15159             }
15160             return p;
15161         } catch (e) {
15162             return null;
15163         }
15164     
15165     },
15166     /***
15167      *
15168      * Range intersection.. the hard stuff...
15169      *  '-1' = before
15170      *  '0' = hits..
15171      *  '1' = after.
15172      *         [ -- selected range --- ]
15173      *   [fail]                        [fail]
15174      *
15175      *    basically..
15176      *      if end is before start or  hits it. fail.
15177      *      if start is after end or hits it fail.
15178      *
15179      *   if either hits (but other is outside. - then it's not 
15180      *   
15181      *    
15182      **/
15183     
15184     
15185     // @see http://www.thismuchiknow.co.uk/?p=64.
15186     rangeIntersectsNode : function(range, node)
15187     {
15188         var nodeRange = node.ownerDocument.createRange();
15189         try {
15190             nodeRange.selectNode(node);
15191         } catch (e) {
15192             nodeRange.selectNodeContents(node);
15193         }
15194     
15195         var rangeStartRange = range.cloneRange();
15196         rangeStartRange.collapse(true);
15197     
15198         var rangeEndRange = range.cloneRange();
15199         rangeEndRange.collapse(false);
15200     
15201         var nodeStartRange = nodeRange.cloneRange();
15202         nodeStartRange.collapse(true);
15203     
15204         var nodeEndRange = nodeRange.cloneRange();
15205         nodeEndRange.collapse(false);
15206     
15207         return rangeStartRange.compareBoundaryPoints(
15208                  Range.START_TO_START, nodeEndRange) == -1 &&
15209                rangeEndRange.compareBoundaryPoints(
15210                  Range.START_TO_START, nodeStartRange) == 1;
15211         
15212          
15213     },
15214     rangeCompareNode : function(range, node)
15215     {
15216         var nodeRange = node.ownerDocument.createRange();
15217         try {
15218             nodeRange.selectNode(node);
15219         } catch (e) {
15220             nodeRange.selectNodeContents(node);
15221         }
15222         
15223         
15224         range.collapse(true);
15225     
15226         nodeRange.collapse(true);
15227      
15228         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15229         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15230          
15231         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15232         
15233         var nodeIsBefore   =  ss == 1;
15234         var nodeIsAfter    = ee == -1;
15235         
15236         if (nodeIsBefore && nodeIsAfter)
15237             return 0; // outer
15238         if (!nodeIsBefore && nodeIsAfter)
15239             return 1; //right trailed.
15240         
15241         if (nodeIsBefore && !nodeIsAfter)
15242             return 2;  // left trailed.
15243         // fully contined.
15244         return 3;
15245     },
15246
15247     // private? - in a new class?
15248     cleanUpPaste :  function()
15249     {
15250         // cleans up the whole document..
15251         Roo.log('cleanuppaste');
15252         
15253         this.cleanUpChildren(this.doc.body);
15254         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15255         if (clean != this.doc.body.innerHTML) {
15256             this.doc.body.innerHTML = clean;
15257         }
15258         
15259     },
15260     
15261     cleanWordChars : function(input) {// change the chars to hex code
15262         var he = Roo.HtmlEditorCore;
15263         
15264         var output = input;
15265         Roo.each(he.swapCodes, function(sw) { 
15266             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15267             
15268             output = output.replace(swapper, sw[1]);
15269         });
15270         
15271         return output;
15272     },
15273     
15274     
15275     cleanUpChildren : function (n)
15276     {
15277         if (!n.childNodes.length) {
15278             return;
15279         }
15280         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15281            this.cleanUpChild(n.childNodes[i]);
15282         }
15283     },
15284     
15285     
15286         
15287     
15288     cleanUpChild : function (node)
15289     {
15290         var ed = this;
15291         //console.log(node);
15292         if (node.nodeName == "#text") {
15293             // clean up silly Windows -- stuff?
15294             return; 
15295         }
15296         if (node.nodeName == "#comment") {
15297             node.parentNode.removeChild(node);
15298             // clean up silly Windows -- stuff?
15299             return; 
15300         }
15301         
15302         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15303             // remove node.
15304             node.parentNode.removeChild(node);
15305             return;
15306             
15307         }
15308         
15309         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15310         
15311         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15312         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15313         
15314         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15315         //    remove_keep_children = true;
15316         //}
15317         
15318         if (remove_keep_children) {
15319             this.cleanUpChildren(node);
15320             // inserts everything just before this node...
15321             while (node.childNodes.length) {
15322                 var cn = node.childNodes[0];
15323                 node.removeChild(cn);
15324                 node.parentNode.insertBefore(cn, node);
15325             }
15326             node.parentNode.removeChild(node);
15327             return;
15328         }
15329         
15330         if (!node.attributes || !node.attributes.length) {
15331             this.cleanUpChildren(node);
15332             return;
15333         }
15334         
15335         function cleanAttr(n,v)
15336         {
15337             
15338             if (v.match(/^\./) || v.match(/^\//)) {
15339                 return;
15340             }
15341             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15342                 return;
15343             }
15344             if (v.match(/^#/)) {
15345                 return;
15346             }
15347 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15348             node.removeAttribute(n);
15349             
15350         }
15351         
15352         function cleanStyle(n,v)
15353         {
15354             if (v.match(/expression/)) { //XSS?? should we even bother..
15355                 node.removeAttribute(n);
15356                 return;
15357             }
15358             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15359             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15360             
15361             
15362             var parts = v.split(/;/);
15363             var clean = [];
15364             
15365             Roo.each(parts, function(p) {
15366                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15367                 if (!p.length) {
15368                     return true;
15369                 }
15370                 var l = p.split(':').shift().replace(/\s+/g,'');
15371                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15372                 
15373                 if ( cblack.indexOf(l) > -1) {
15374 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15375                     //node.removeAttribute(n);
15376                     return true;
15377                 }
15378                 //Roo.log()
15379                 // only allow 'c whitelisted system attributes'
15380                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15381 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15382                     //node.removeAttribute(n);
15383                     return true;
15384                 }
15385                 
15386                 
15387                  
15388                 
15389                 clean.push(p);
15390                 return true;
15391             });
15392             if (clean.length) { 
15393                 node.setAttribute(n, clean.join(';'));
15394             } else {
15395                 node.removeAttribute(n);
15396             }
15397             
15398         }
15399         
15400         
15401         for (var i = node.attributes.length-1; i > -1 ; i--) {
15402             var a = node.attributes[i];
15403             //console.log(a);
15404             
15405             if (a.name.toLowerCase().substr(0,2)=='on')  {
15406                 node.removeAttribute(a.name);
15407                 continue;
15408             }
15409             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15410                 node.removeAttribute(a.name);
15411                 continue;
15412             }
15413             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15414                 cleanAttr(a.name,a.value); // fixme..
15415                 continue;
15416             }
15417             if (a.name == 'style') {
15418                 cleanStyle(a.name,a.value);
15419                 continue;
15420             }
15421             /// clean up MS crap..
15422             // tecnically this should be a list of valid class'es..
15423             
15424             
15425             if (a.name == 'class') {
15426                 if (a.value.match(/^Mso/)) {
15427                     node.className = '';
15428                 }
15429                 
15430                 if (a.value.match(/body/)) {
15431                     node.className = '';
15432                 }
15433                 continue;
15434             }
15435             
15436             // style cleanup!?
15437             // class cleanup?
15438             
15439         }
15440         
15441         
15442         this.cleanUpChildren(node);
15443         
15444         
15445     },
15446     /**
15447      * Clean up MS wordisms...
15448      */
15449     cleanWord : function(node)
15450     {
15451         var _t = this;
15452         var cleanWordChildren = function()
15453         {
15454             if (!node.childNodes.length) {
15455                 return;
15456             }
15457             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15458                _t.cleanWord(node.childNodes[i]);
15459             }
15460         }
15461         
15462         
15463         if (!node) {
15464             this.cleanWord(this.doc.body);
15465             return;
15466         }
15467         if (node.nodeName == "#text") {
15468             // clean up silly Windows -- stuff?
15469             return; 
15470         }
15471         if (node.nodeName == "#comment") {
15472             node.parentNode.removeChild(node);
15473             // clean up silly Windows -- stuff?
15474             return; 
15475         }
15476         
15477         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15478             node.parentNode.removeChild(node);
15479             return;
15480         }
15481         
15482         // remove - but keep children..
15483         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15484             while (node.childNodes.length) {
15485                 var cn = node.childNodes[0];
15486                 node.removeChild(cn);
15487                 node.parentNode.insertBefore(cn, node);
15488             }
15489             node.parentNode.removeChild(node);
15490             cleanWordChildren();
15491             return;
15492         }
15493         // clean styles
15494         if (node.className.length) {
15495             
15496             var cn = node.className.split(/\W+/);
15497             var cna = [];
15498             Roo.each(cn, function(cls) {
15499                 if (cls.match(/Mso[a-zA-Z]+/)) {
15500                     return;
15501                 }
15502                 cna.push(cls);
15503             });
15504             node.className = cna.length ? cna.join(' ') : '';
15505             if (!cna.length) {
15506                 node.removeAttribute("class");
15507             }
15508         }
15509         
15510         if (node.hasAttribute("lang")) {
15511             node.removeAttribute("lang");
15512         }
15513         
15514         if (node.hasAttribute("style")) {
15515             
15516             var styles = node.getAttribute("style").split(";");
15517             var nstyle = [];
15518             Roo.each(styles, function(s) {
15519                 if (!s.match(/:/)) {
15520                     return;
15521                 }
15522                 var kv = s.split(":");
15523                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15524                     return;
15525                 }
15526                 // what ever is left... we allow.
15527                 nstyle.push(s);
15528             });
15529             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15530             if (!nstyle.length) {
15531                 node.removeAttribute('style');
15532             }
15533         }
15534         
15535         cleanWordChildren();
15536         
15537         
15538     },
15539     domToHTML : function(currentElement, depth, nopadtext) {
15540         
15541             depth = depth || 0;
15542             nopadtext = nopadtext || false;
15543         
15544             if (!currentElement) {
15545                 return this.domToHTML(this.doc.body);
15546             }
15547             
15548             //Roo.log(currentElement);
15549             var j;
15550             var allText = false;
15551             var nodeName = currentElement.nodeName;
15552             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15553             
15554             if  (nodeName == '#text') {
15555                 return currentElement.nodeValue;
15556             }
15557             
15558             
15559             var ret = '';
15560             if (nodeName != 'BODY') {
15561                  
15562                 var i = 0;
15563                 // Prints the node tagName, such as <A>, <IMG>, etc
15564                 if (tagName) {
15565                     var attr = [];
15566                     for(i = 0; i < currentElement.attributes.length;i++) {
15567                         // quoting?
15568                         var aname = currentElement.attributes.item(i).name;
15569                         if (!currentElement.attributes.item(i).value.length) {
15570                             continue;
15571                         }
15572                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15573                     }
15574                     
15575                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15576                 } 
15577                 else {
15578                     
15579                     // eack
15580                 }
15581             } else {
15582                 tagName = false;
15583             }
15584             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15585                 return ret;
15586             }
15587             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15588                 nopadtext = true;
15589             }
15590             
15591             
15592             // Traverse the tree
15593             i = 0;
15594             var currentElementChild = currentElement.childNodes.item(i);
15595             var allText = true;
15596             var innerHTML  = '';
15597             lastnode = '';
15598             while (currentElementChild) {
15599                 // Formatting code (indent the tree so it looks nice on the screen)
15600                 var nopad = nopadtext;
15601                 if (lastnode == 'SPAN') {
15602                     nopad  = true;
15603                 }
15604                 // text
15605                 if  (currentElementChild.nodeName == '#text') {
15606                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15607                     if (!nopad && toadd.length > 80) {
15608                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15609                     }
15610                     innerHTML  += toadd;
15611                     
15612                     i++;
15613                     currentElementChild = currentElement.childNodes.item(i);
15614                     lastNode = '';
15615                     continue;
15616                 }
15617                 allText = false;
15618                 
15619                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15620                     
15621                 // Recursively traverse the tree structure of the child node
15622                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15623                 lastnode = currentElementChild.nodeName;
15624                 i++;
15625                 currentElementChild=currentElement.childNodes.item(i);
15626             }
15627             
15628             ret += innerHTML;
15629             
15630             if (!allText) {
15631                     // The remaining code is mostly for formatting the tree
15632                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15633             }
15634             
15635             
15636             if (tagName) {
15637                 ret+= "</"+tagName+">";
15638             }
15639             return ret;
15640             
15641         }
15642     
15643     // hide stuff that is not compatible
15644     /**
15645      * @event blur
15646      * @hide
15647      */
15648     /**
15649      * @event change
15650      * @hide
15651      */
15652     /**
15653      * @event focus
15654      * @hide
15655      */
15656     /**
15657      * @event specialkey
15658      * @hide
15659      */
15660     /**
15661      * @cfg {String} fieldClass @hide
15662      */
15663     /**
15664      * @cfg {String} focusClass @hide
15665      */
15666     /**
15667      * @cfg {String} autoCreate @hide
15668      */
15669     /**
15670      * @cfg {String} inputType @hide
15671      */
15672     /**
15673      * @cfg {String} invalidClass @hide
15674      */
15675     /**
15676      * @cfg {String} invalidText @hide
15677      */
15678     /**
15679      * @cfg {String} msgFx @hide
15680      */
15681     /**
15682      * @cfg {String} validateOnBlur @hide
15683      */
15684 });
15685
15686 Roo.HtmlEditorCore.white = [
15687         'area', 'br', 'img', 'input', 'hr', 'wbr',
15688         
15689        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15690        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15691        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15692        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15693        'table',   'ul',         'xmp', 
15694        
15695        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15696       'thead',   'tr', 
15697      
15698       'dir', 'menu', 'ol', 'ul', 'dl',
15699        
15700       'embed',  'object'
15701 ];
15702
15703
15704 Roo.HtmlEditorCore.black = [
15705     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15706         'applet', // 
15707         'base',   'basefont', 'bgsound', 'blink',  'body', 
15708         'frame',  'frameset', 'head',    'html',   'ilayer', 
15709         'iframe', 'layer',  'link',     'meta',    'object',   
15710         'script', 'style' ,'title',  'xml' // clean later..
15711 ];
15712 Roo.HtmlEditorCore.clean = [
15713     'script', 'style', 'title', 'xml'
15714 ];
15715 Roo.HtmlEditorCore.remove = [
15716     'font'
15717 ];
15718 // attributes..
15719
15720 Roo.HtmlEditorCore.ablack = [
15721     'on'
15722 ];
15723     
15724 Roo.HtmlEditorCore.aclean = [ 
15725     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15726 ];
15727
15728 // protocols..
15729 Roo.HtmlEditorCore.pwhite= [
15730         'http',  'https',  'mailto'
15731 ];
15732
15733 // white listed style attributes.
15734 Roo.HtmlEditorCore.cwhite= [
15735       //  'text-align', /// default is to allow most things..
15736       
15737          
15738 //        'font-size'//??
15739 ];
15740
15741 // black listed style attributes.
15742 Roo.HtmlEditorCore.cblack= [
15743       //  'font-size' -- this can be set by the project 
15744 ];
15745
15746
15747 Roo.HtmlEditorCore.swapCodes   =[ 
15748     [    8211, "--" ], 
15749     [    8212, "--" ], 
15750     [    8216,  "'" ],  
15751     [    8217, "'" ],  
15752     [    8220, '"' ],  
15753     [    8221, '"' ],  
15754     [    8226, "*" ],  
15755     [    8230, "..." ]
15756 ]; 
15757
15758     /*
15759  * - LGPL
15760  *
15761  * HtmlEditor
15762  * 
15763  */
15764
15765 /**
15766  * @class Roo.bootstrap.HtmlEditor
15767  * @extends Roo.bootstrap.TextArea
15768  * Bootstrap HtmlEditor class
15769
15770  * @constructor
15771  * Create a new HtmlEditor
15772  * @param {Object} config The config object
15773  */
15774
15775 Roo.bootstrap.HtmlEditor = function(config){
15776     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15777     if (!this.toolbars) {
15778         this.toolbars = [];
15779     }
15780     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15781     this.addEvents({
15782             /**
15783              * @event initialize
15784              * Fires when the editor is fully initialized (including the iframe)
15785              * @param {HtmlEditor} this
15786              */
15787             initialize: true,
15788             /**
15789              * @event activate
15790              * Fires when the editor is first receives the focus. Any insertion must wait
15791              * until after this event.
15792              * @param {HtmlEditor} this
15793              */
15794             activate: true,
15795              /**
15796              * @event beforesync
15797              * Fires before the textarea is updated with content from the editor iframe. Return false
15798              * to cancel the sync.
15799              * @param {HtmlEditor} this
15800              * @param {String} html
15801              */
15802             beforesync: true,
15803              /**
15804              * @event beforepush
15805              * Fires before the iframe editor is updated with content from the textarea. Return false
15806              * to cancel the push.
15807              * @param {HtmlEditor} this
15808              * @param {String} html
15809              */
15810             beforepush: true,
15811              /**
15812              * @event sync
15813              * Fires when the textarea is updated with content from the editor iframe.
15814              * @param {HtmlEditor} this
15815              * @param {String} html
15816              */
15817             sync: true,
15818              /**
15819              * @event push
15820              * Fires when the iframe editor is updated with content from the textarea.
15821              * @param {HtmlEditor} this
15822              * @param {String} html
15823              */
15824             push: true,
15825              /**
15826              * @event editmodechange
15827              * Fires when the editor switches edit modes
15828              * @param {HtmlEditor} this
15829              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15830              */
15831             editmodechange: true,
15832             /**
15833              * @event editorevent
15834              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15835              * @param {HtmlEditor} this
15836              */
15837             editorevent: true,
15838             /**
15839              * @event firstfocus
15840              * Fires when on first focus - needed by toolbars..
15841              * @param {HtmlEditor} this
15842              */
15843             firstfocus: true,
15844             /**
15845              * @event autosave
15846              * Auto save the htmlEditor value as a file into Events
15847              * @param {HtmlEditor} this
15848              */
15849             autosave: true,
15850             /**
15851              * @event savedpreview
15852              * preview the saved version of htmlEditor
15853              * @param {HtmlEditor} this
15854              */
15855             savedpreview: true
15856         });
15857 };
15858
15859
15860 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15861     
15862     
15863       /**
15864      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15865      */
15866     toolbars : false,
15867    
15868      /**
15869      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15870      *                        Roo.resizable.
15871      */
15872     resizable : false,
15873      /**
15874      * @cfg {Number} height (in pixels)
15875      */   
15876     height: 300,
15877    /**
15878      * @cfg {Number} width (in pixels)
15879      */   
15880     width: false,
15881     
15882     /**
15883      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15884      * 
15885      */
15886     stylesheets: false,
15887     
15888     // id of frame..
15889     frameId: false,
15890     
15891     // private properties
15892     validationEvent : false,
15893     deferHeight: true,
15894     initialized : false,
15895     activated : false,
15896     
15897     onFocus : Roo.emptyFn,
15898     iframePad:3,
15899     hideMode:'offsets',
15900     
15901     
15902     tbContainer : false,
15903     
15904     toolbarContainer :function() {
15905         return this.wrap.select('.x-html-editor-tb',true).first();
15906     },
15907
15908     /**
15909      * Protected method that will not generally be called directly. It
15910      * is called when the editor creates its toolbar. Override this method if you need to
15911      * add custom toolbar buttons.
15912      * @param {HtmlEditor} editor
15913      */
15914     createToolbar : function(){
15915         
15916         Roo.log("create toolbars");
15917         
15918         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15919         this.toolbars[0].render(this.toolbarContainer());
15920         
15921         return;
15922         
15923 //        if (!editor.toolbars || !editor.toolbars.length) {
15924 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15925 //        }
15926 //        
15927 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15928 //            editor.toolbars[i] = Roo.factory(
15929 //                    typeof(editor.toolbars[i]) == 'string' ?
15930 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15931 //                Roo.bootstrap.HtmlEditor);
15932 //            editor.toolbars[i].init(editor);
15933 //        }
15934     },
15935
15936      
15937     // private
15938     onRender : function(ct, position)
15939     {
15940        // Roo.log("Call onRender: " + this.xtype);
15941         var _t = this;
15942         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15943       
15944         this.wrap = this.inputEl().wrap({
15945             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15946         });
15947         
15948         this.editorcore.onRender(ct, position);
15949          
15950         if (this.resizable) {
15951             this.resizeEl = new Roo.Resizable(this.wrap, {
15952                 pinned : true,
15953                 wrap: true,
15954                 dynamic : true,
15955                 minHeight : this.height,
15956                 height: this.height,
15957                 handles : this.resizable,
15958                 width: this.width,
15959                 listeners : {
15960                     resize : function(r, w, h) {
15961                         _t.onResize(w,h); // -something
15962                     }
15963                 }
15964             });
15965             
15966         }
15967         this.createToolbar(this);
15968        
15969         
15970         if(!this.width && this.resizable){
15971             this.setSize(this.wrap.getSize());
15972         }
15973         if (this.resizeEl) {
15974             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15975             // should trigger onReize..
15976         }
15977         
15978     },
15979
15980     // private
15981     onResize : function(w, h)
15982     {
15983         Roo.log('resize: ' +w + ',' + h );
15984         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15985         var ew = false;
15986         var eh = false;
15987         
15988         if(this.inputEl() ){
15989             if(typeof w == 'number'){
15990                 var aw = w - this.wrap.getFrameWidth('lr');
15991                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15992                 ew = aw;
15993             }
15994             if(typeof h == 'number'){
15995                  var tbh = -11;  // fixme it needs to tool bar size!
15996                 for (var i =0; i < this.toolbars.length;i++) {
15997                     // fixme - ask toolbars for heights?
15998                     tbh += this.toolbars[i].el.getHeight();
15999                     //if (this.toolbars[i].footer) {
16000                     //    tbh += this.toolbars[i].footer.el.getHeight();
16001                     //}
16002                 }
16003               
16004                 
16005                 
16006                 
16007                 
16008                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16009                 ah -= 5; // knock a few pixes off for look..
16010                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16011                 var eh = ah;
16012             }
16013         }
16014         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16015         this.editorcore.onResize(ew,eh);
16016         
16017     },
16018
16019     /**
16020      * Toggles the editor between standard and source edit mode.
16021      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16022      */
16023     toggleSourceEdit : function(sourceEditMode)
16024     {
16025         this.editorcore.toggleSourceEdit(sourceEditMode);
16026         
16027         if(this.editorcore.sourceEditMode){
16028             Roo.log('editor - showing textarea');
16029             
16030 //            Roo.log('in');
16031 //            Roo.log(this.syncValue());
16032             this.syncValue();
16033             this.inputEl().removeClass('hide');
16034             this.inputEl().dom.removeAttribute('tabIndex');
16035             this.inputEl().focus();
16036         }else{
16037             Roo.log('editor - hiding textarea');
16038 //            Roo.log('out')
16039 //            Roo.log(this.pushValue()); 
16040             this.pushValue();
16041             
16042             this.inputEl().addClass('hide');
16043             this.inputEl().dom.setAttribute('tabIndex', -1);
16044             //this.deferFocus();
16045         }
16046          
16047         if(this.resizable){
16048             this.setSize(this.wrap.getSize());
16049         }
16050         
16051         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16052     },
16053  
16054     // private (for BoxComponent)
16055     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16056
16057     // private (for BoxComponent)
16058     getResizeEl : function(){
16059         return this.wrap;
16060     },
16061
16062     // private (for BoxComponent)
16063     getPositionEl : function(){
16064         return this.wrap;
16065     },
16066
16067     // private
16068     initEvents : function(){
16069         this.originalValue = this.getValue();
16070     },
16071
16072 //    /**
16073 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16074 //     * @method
16075 //     */
16076 //    markInvalid : Roo.emptyFn,
16077 //    /**
16078 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16079 //     * @method
16080 //     */
16081 //    clearInvalid : Roo.emptyFn,
16082
16083     setValue : function(v){
16084         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16085         this.editorcore.pushValue();
16086     },
16087
16088      
16089     // private
16090     deferFocus : function(){
16091         this.focus.defer(10, this);
16092     },
16093
16094     // doc'ed in Field
16095     focus : function(){
16096         this.editorcore.focus();
16097         
16098     },
16099       
16100
16101     // private
16102     onDestroy : function(){
16103         
16104         
16105         
16106         if(this.rendered){
16107             
16108             for (var i =0; i < this.toolbars.length;i++) {
16109                 // fixme - ask toolbars for heights?
16110                 this.toolbars[i].onDestroy();
16111             }
16112             
16113             this.wrap.dom.innerHTML = '';
16114             this.wrap.remove();
16115         }
16116     },
16117
16118     // private
16119     onFirstFocus : function(){
16120         //Roo.log("onFirstFocus");
16121         this.editorcore.onFirstFocus();
16122          for (var i =0; i < this.toolbars.length;i++) {
16123             this.toolbars[i].onFirstFocus();
16124         }
16125         
16126     },
16127     
16128     // private
16129     syncValue : function()
16130     {   
16131         this.editorcore.syncValue();
16132     },
16133     
16134     pushValue : function()
16135     {   
16136         this.editorcore.pushValue();
16137     }
16138      
16139     
16140     // hide stuff that is not compatible
16141     /**
16142      * @event blur
16143      * @hide
16144      */
16145     /**
16146      * @event change
16147      * @hide
16148      */
16149     /**
16150      * @event focus
16151      * @hide
16152      */
16153     /**
16154      * @event specialkey
16155      * @hide
16156      */
16157     /**
16158      * @cfg {String} fieldClass @hide
16159      */
16160     /**
16161      * @cfg {String} focusClass @hide
16162      */
16163     /**
16164      * @cfg {String} autoCreate @hide
16165      */
16166     /**
16167      * @cfg {String} inputType @hide
16168      */
16169     /**
16170      * @cfg {String} invalidClass @hide
16171      */
16172     /**
16173      * @cfg {String} invalidText @hide
16174      */
16175     /**
16176      * @cfg {String} msgFx @hide
16177      */
16178     /**
16179      * @cfg {String} validateOnBlur @hide
16180      */
16181 });
16182  
16183     
16184    
16185    
16186    
16187       
16188
16189 /**
16190  * @class Roo.bootstrap.HtmlEditorToolbar1
16191  * Basic Toolbar
16192  * 
16193  * Usage:
16194  *
16195  new Roo.bootstrap.HtmlEditor({
16196     ....
16197     toolbars : [
16198         new Roo.bootstrap.HtmlEditorToolbar1({
16199             disable : { fonts: 1 , format: 1, ..., ... , ...],
16200             btns : [ .... ]
16201         })
16202     }
16203      
16204  * 
16205  * @cfg {Object} disable List of elements to disable..
16206  * @cfg {Array} btns List of additional buttons.
16207  * 
16208  * 
16209  * NEEDS Extra CSS? 
16210  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16211  */
16212  
16213 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
16214 {
16215     
16216     Roo.apply(this, config);
16217     
16218     // default disabled, based on 'good practice'..
16219     this.disable = this.disable || {};
16220     Roo.applyIf(this.disable, {
16221         fontSize : true,
16222         colors : true,
16223         specialElements : true
16224     });
16225     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
16226     
16227     this.editor = config.editor;
16228     this.editorcore = config.editor.editorcore;
16229     
16230     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16231     
16232     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16233     // dont call parent... till later.
16234 }
16235 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16236     
16237     
16238     bar : true,
16239     
16240     editor : false,
16241     editorcore : false,
16242     
16243     
16244     formats : [
16245         "p" ,  
16246         "h1","h2","h3","h4","h5","h6", 
16247         "pre", "code", 
16248         "abbr", "acronym", "address", "cite", "samp", "var",
16249         'div','span'
16250     ],
16251     
16252     onRender : function(ct, position)
16253     {
16254        // Roo.log("Call onRender: " + this.xtype);
16255         
16256        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16257        Roo.log(this.el);
16258        this.el.dom.style.marginBottom = '0';
16259        var _this = this;
16260        var editorcore = this.editorcore;
16261        var editor= this.editor;
16262        
16263        var children = [];
16264        var btn = function(id,cmd , toggle, handler){
16265        
16266             var  event = toggle ? 'toggle' : 'click';
16267        
16268             var a = {
16269                 size : 'sm',
16270                 xtype: 'Button',
16271                 xns: Roo.bootstrap,
16272                 glyphicon : id,
16273                 cmd : id || cmd,
16274                 enableToggle:toggle !== false,
16275                 //html : 'submit'
16276                 pressed : toggle ? false : null,
16277                 listeners : {}
16278             }
16279             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16280                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16281             }
16282             children.push(a);
16283             return a;
16284        }
16285         
16286         var style = {
16287                 xtype: 'Button',
16288                 size : 'sm',
16289                 xns: Roo.bootstrap,
16290                 glyphicon : 'font',
16291                 //html : 'submit'
16292                 menu : {
16293                     xtype: 'Menu',
16294                     xns: Roo.bootstrap,
16295                     items:  []
16296                 }
16297         };
16298         Roo.each(this.formats, function(f) {
16299             style.menu.items.push({
16300                 xtype :'MenuItem',
16301                 xns: Roo.bootstrap,
16302                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16303                 tagname : f,
16304                 listeners : {
16305                     click : function()
16306                     {
16307                         editorcore.insertTag(this.tagname);
16308                         editor.focus();
16309                     }
16310                 }
16311                 
16312             });
16313         });
16314          children.push(style);   
16315             
16316             
16317         btn('bold',false,true);
16318         btn('italic',false,true);
16319         btn('align-left', 'justifyleft',true);
16320         btn('align-center', 'justifycenter',true);
16321         btn('align-right' , 'justifyright',true);
16322         btn('link', false, false, function(btn) {
16323             //Roo.log("create link?");
16324             var url = prompt(this.createLinkText, this.defaultLinkValue);
16325             if(url && url != 'http:/'+'/'){
16326                 this.editorcore.relayCmd('createlink', url);
16327             }
16328         }),
16329         btn('list','insertunorderedlist',true);
16330         btn('pencil', false,true, function(btn){
16331                 Roo.log(this);
16332                 
16333                 this.toggleSourceEdit(btn.pressed);
16334         });
16335         /*
16336         var cog = {
16337                 xtype: 'Button',
16338                 size : 'sm',
16339                 xns: Roo.bootstrap,
16340                 glyphicon : 'cog',
16341                 //html : 'submit'
16342                 menu : {
16343                     xtype: 'Menu',
16344                     xns: Roo.bootstrap,
16345                     items:  []
16346                 }
16347         };
16348         
16349         cog.menu.items.push({
16350             xtype :'MenuItem',
16351             xns: Roo.bootstrap,
16352             html : Clean styles,
16353             tagname : f,
16354             listeners : {
16355                 click : function()
16356                 {
16357                     editorcore.insertTag(this.tagname);
16358                     editor.focus();
16359                 }
16360             }
16361             
16362         });
16363        */
16364         
16365          
16366        this.xtype = 'NavSimplebar';
16367         
16368         for(var i=0;i< children.length;i++) {
16369             
16370             this.buttons.add(this.addxtypeChild(children[i]));
16371             
16372         }
16373         
16374         editor.on('editorevent', this.updateToolbar, this);
16375     },
16376     onBtnClick : function(id)
16377     {
16378        this.editorcore.relayCmd(id);
16379        this.editorcore.focus();
16380     },
16381     
16382     /**
16383      * Protected method that will not generally be called directly. It triggers
16384      * a toolbar update by reading the markup state of the current selection in the editor.
16385      */
16386     updateToolbar: function(){
16387
16388         if(!this.editorcore.activated){
16389             this.editor.onFirstFocus(); // is this neeed?
16390             return;
16391         }
16392
16393         var btns = this.buttons; 
16394         var doc = this.editorcore.doc;
16395         btns.get('bold').setActive(doc.queryCommandState('bold'));
16396         btns.get('italic').setActive(doc.queryCommandState('italic'));
16397         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16398         
16399         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16400         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16401         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16402         
16403         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16404         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16405          /*
16406         
16407         var ans = this.editorcore.getAllAncestors();
16408         if (this.formatCombo) {
16409             
16410             
16411             var store = this.formatCombo.store;
16412             this.formatCombo.setValue("");
16413             for (var i =0; i < ans.length;i++) {
16414                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16415                     // select it..
16416                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16417                     break;
16418                 }
16419             }
16420         }
16421         
16422         
16423         
16424         // hides menus... - so this cant be on a menu...
16425         Roo.bootstrap.MenuMgr.hideAll();
16426         */
16427         Roo.bootstrap.MenuMgr.hideAll();
16428         //this.editorsyncValue();
16429     },
16430     onFirstFocus: function() {
16431         this.buttons.each(function(item){
16432            item.enable();
16433         });
16434     },
16435     toggleSourceEdit : function(sourceEditMode){
16436         
16437           
16438         if(sourceEditMode){
16439             Roo.log("disabling buttons");
16440            this.buttons.each( function(item){
16441                 if(item.cmd != 'pencil'){
16442                     item.disable();
16443                 }
16444             });
16445           
16446         }else{
16447             Roo.log("enabling buttons");
16448             if(this.editorcore.initialized){
16449                 this.buttons.each( function(item){
16450                     item.enable();
16451                 });
16452             }
16453             
16454         }
16455         Roo.log("calling toggole on editor");
16456         // tell the editor that it's been pressed..
16457         this.editor.toggleSourceEdit(sourceEditMode);
16458        
16459     }
16460 });
16461
16462
16463
16464
16465
16466 /**
16467  * @class Roo.bootstrap.Table.AbstractSelectionModel
16468  * @extends Roo.util.Observable
16469  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16470  * implemented by descendant classes.  This class should not be directly instantiated.
16471  * @constructor
16472  */
16473 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16474     this.locked = false;
16475     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16476 };
16477
16478
16479 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16480     /** @ignore Called by the grid automatically. Do not call directly. */
16481     init : function(grid){
16482         this.grid = grid;
16483         this.initEvents();
16484     },
16485
16486     /**
16487      * Locks the selections.
16488      */
16489     lock : function(){
16490         this.locked = true;
16491     },
16492
16493     /**
16494      * Unlocks the selections.
16495      */
16496     unlock : function(){
16497         this.locked = false;
16498     },
16499
16500     /**
16501      * Returns true if the selections are locked.
16502      * @return {Boolean}
16503      */
16504     isLocked : function(){
16505         return this.locked;
16506     }
16507 });
16508 /**
16509  * @class Roo.bootstrap.Table.ColumnModel
16510  * @extends Roo.util.Observable
16511  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16512  * the columns in the table.
16513  
16514  * @constructor
16515  * @param {Object} config An Array of column config objects. See this class's
16516  * config objects for details.
16517 */
16518 Roo.bootstrap.Table.ColumnModel = function(config){
16519         /**
16520      * The config passed into the constructor
16521      */
16522     this.config = config;
16523     this.lookup = {};
16524
16525     // if no id, create one
16526     // if the column does not have a dataIndex mapping,
16527     // map it to the order it is in the config
16528     for(var i = 0, len = config.length; i < len; i++){
16529         var c = config[i];
16530         if(typeof c.dataIndex == "undefined"){
16531             c.dataIndex = i;
16532         }
16533         if(typeof c.renderer == "string"){
16534             c.renderer = Roo.util.Format[c.renderer];
16535         }
16536         if(typeof c.id == "undefined"){
16537             c.id = Roo.id();
16538         }
16539 //        if(c.editor && c.editor.xtype){
16540 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16541 //        }
16542 //        if(c.editor && c.editor.isFormField){
16543 //            c.editor = new Roo.grid.GridEditor(c.editor);
16544 //        }
16545
16546         this.lookup[c.id] = c;
16547     }
16548
16549     /**
16550      * The width of columns which have no width specified (defaults to 100)
16551      * @type Number
16552      */
16553     this.defaultWidth = 100;
16554
16555     /**
16556      * Default sortable of columns which have no sortable specified (defaults to false)
16557      * @type Boolean
16558      */
16559     this.defaultSortable = false;
16560
16561     this.addEvents({
16562         /**
16563              * @event widthchange
16564              * Fires when the width of a column changes.
16565              * @param {ColumnModel} this
16566              * @param {Number} columnIndex The column index
16567              * @param {Number} newWidth The new width
16568              */
16569             "widthchange": true,
16570         /**
16571              * @event headerchange
16572              * Fires when the text of a header changes.
16573              * @param {ColumnModel} this
16574              * @param {Number} columnIndex The column index
16575              * @param {Number} newText The new header text
16576              */
16577             "headerchange": true,
16578         /**
16579              * @event hiddenchange
16580              * Fires when a column is hidden or "unhidden".
16581              * @param {ColumnModel} this
16582              * @param {Number} columnIndex The column index
16583              * @param {Boolean} hidden true if hidden, false otherwise
16584              */
16585             "hiddenchange": true,
16586             /**
16587          * @event columnmoved
16588          * Fires when a column is moved.
16589          * @param {ColumnModel} this
16590          * @param {Number} oldIndex
16591          * @param {Number} newIndex
16592          */
16593         "columnmoved" : true,
16594         /**
16595          * @event columlockchange
16596          * Fires when a column's locked state is changed
16597          * @param {ColumnModel} this
16598          * @param {Number} colIndex
16599          * @param {Boolean} locked true if locked
16600          */
16601         "columnlockchange" : true
16602     });
16603     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16604 };
16605 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16606     /**
16607      * @cfg {String} header The header text to display in the Grid view.
16608      */
16609     /**
16610      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16611      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16612      * specified, the column's index is used as an index into the Record's data Array.
16613      */
16614     /**
16615      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16616      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16617      */
16618     /**
16619      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16620      * Defaults to the value of the {@link #defaultSortable} property.
16621      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16622      */
16623     /**
16624      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16625      */
16626     /**
16627      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16628      */
16629     /**
16630      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16631      */
16632     /**
16633      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16634      */
16635     /**
16636      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16637      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16638      * default renderer uses the raw data value.
16639      */
16640     /**
16641      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16642      */
16643
16644     /**
16645      * Returns the id of the column at the specified index.
16646      * @param {Number} index The column index
16647      * @return {String} the id
16648      */
16649     getColumnId : function(index){
16650         return this.config[index].id;
16651     },
16652
16653     /**
16654      * Returns the column for a specified id.
16655      * @param {String} id The column id
16656      * @return {Object} the column
16657      */
16658     getColumnById : function(id){
16659         return this.lookup[id];
16660     },
16661
16662     
16663     /**
16664      * Returns the column for a specified dataIndex.
16665      * @param {String} dataIndex The column dataIndex
16666      * @return {Object|Boolean} the column or false if not found
16667      */
16668     getColumnByDataIndex: function(dataIndex){
16669         var index = this.findColumnIndex(dataIndex);
16670         return index > -1 ? this.config[index] : false;
16671     },
16672     
16673     /**
16674      * Returns the index for a specified column id.
16675      * @param {String} id The column id
16676      * @return {Number} the index, or -1 if not found
16677      */
16678     getIndexById : function(id){
16679         for(var i = 0, len = this.config.length; i < len; i++){
16680             if(this.config[i].id == id){
16681                 return i;
16682             }
16683         }
16684         return -1;
16685     },
16686     
16687     /**
16688      * Returns the index for a specified column dataIndex.
16689      * @param {String} dataIndex The column dataIndex
16690      * @return {Number} the index, or -1 if not found
16691      */
16692     
16693     findColumnIndex : function(dataIndex){
16694         for(var i = 0, len = this.config.length; i < len; i++){
16695             if(this.config[i].dataIndex == dataIndex){
16696                 return i;
16697             }
16698         }
16699         return -1;
16700     },
16701     
16702     
16703     moveColumn : function(oldIndex, newIndex){
16704         var c = this.config[oldIndex];
16705         this.config.splice(oldIndex, 1);
16706         this.config.splice(newIndex, 0, c);
16707         this.dataMap = null;
16708         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16709     },
16710
16711     isLocked : function(colIndex){
16712         return this.config[colIndex].locked === true;
16713     },
16714
16715     setLocked : function(colIndex, value, suppressEvent){
16716         if(this.isLocked(colIndex) == value){
16717             return;
16718         }
16719         this.config[colIndex].locked = value;
16720         if(!suppressEvent){
16721             this.fireEvent("columnlockchange", this, colIndex, value);
16722         }
16723     },
16724
16725     getTotalLockedWidth : function(){
16726         var totalWidth = 0;
16727         for(var i = 0; i < this.config.length; i++){
16728             if(this.isLocked(i) && !this.isHidden(i)){
16729                 this.totalWidth += this.getColumnWidth(i);
16730             }
16731         }
16732         return totalWidth;
16733     },
16734
16735     getLockedCount : function(){
16736         for(var i = 0, len = this.config.length; i < len; i++){
16737             if(!this.isLocked(i)){
16738                 return i;
16739             }
16740         }
16741     },
16742
16743     /**
16744      * Returns the number of columns.
16745      * @return {Number}
16746      */
16747     getColumnCount : function(visibleOnly){
16748         if(visibleOnly === true){
16749             var c = 0;
16750             for(var i = 0, len = this.config.length; i < len; i++){
16751                 if(!this.isHidden(i)){
16752                     c++;
16753                 }
16754             }
16755             return c;
16756         }
16757         return this.config.length;
16758     },
16759
16760     /**
16761      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16762      * @param {Function} fn
16763      * @param {Object} scope (optional)
16764      * @return {Array} result
16765      */
16766     getColumnsBy : function(fn, scope){
16767         var r = [];
16768         for(var i = 0, len = this.config.length; i < len; i++){
16769             var c = this.config[i];
16770             if(fn.call(scope||this, c, i) === true){
16771                 r[r.length] = c;
16772             }
16773         }
16774         return r;
16775     },
16776
16777     /**
16778      * Returns true if the specified column is sortable.
16779      * @param {Number} col The column index
16780      * @return {Boolean}
16781      */
16782     isSortable : function(col){
16783         if(typeof this.config[col].sortable == "undefined"){
16784             return this.defaultSortable;
16785         }
16786         return this.config[col].sortable;
16787     },
16788
16789     /**
16790      * Returns the rendering (formatting) function defined for the column.
16791      * @param {Number} col The column index.
16792      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16793      */
16794     getRenderer : function(col){
16795         if(!this.config[col].renderer){
16796             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16797         }
16798         return this.config[col].renderer;
16799     },
16800
16801     /**
16802      * Sets the rendering (formatting) function for a column.
16803      * @param {Number} col The column index
16804      * @param {Function} fn The function to use to process the cell's raw data
16805      * to return HTML markup for the grid view. The render function is called with
16806      * the following parameters:<ul>
16807      * <li>Data value.</li>
16808      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16809      * <li>css A CSS style string to apply to the table cell.</li>
16810      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16811      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16812      * <li>Row index</li>
16813      * <li>Column index</li>
16814      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16815      */
16816     setRenderer : function(col, fn){
16817         this.config[col].renderer = fn;
16818     },
16819
16820     /**
16821      * Returns the width for the specified column.
16822      * @param {Number} col The column index
16823      * @return {Number}
16824      */
16825     getColumnWidth : function(col){
16826         return this.config[col].width * 1 || this.defaultWidth;
16827     },
16828
16829     /**
16830      * Sets the width for a column.
16831      * @param {Number} col The column index
16832      * @param {Number} width The new width
16833      */
16834     setColumnWidth : function(col, width, suppressEvent){
16835         this.config[col].width = width;
16836         this.totalWidth = null;
16837         if(!suppressEvent){
16838              this.fireEvent("widthchange", this, col, width);
16839         }
16840     },
16841
16842     /**
16843      * Returns the total width of all columns.
16844      * @param {Boolean} includeHidden True to include hidden column widths
16845      * @return {Number}
16846      */
16847     getTotalWidth : function(includeHidden){
16848         if(!this.totalWidth){
16849             this.totalWidth = 0;
16850             for(var i = 0, len = this.config.length; i < len; i++){
16851                 if(includeHidden || !this.isHidden(i)){
16852                     this.totalWidth += this.getColumnWidth(i);
16853                 }
16854             }
16855         }
16856         return this.totalWidth;
16857     },
16858
16859     /**
16860      * Returns the header for the specified column.
16861      * @param {Number} col The column index
16862      * @return {String}
16863      */
16864     getColumnHeader : function(col){
16865         return this.config[col].header;
16866     },
16867
16868     /**
16869      * Sets the header for a column.
16870      * @param {Number} col The column index
16871      * @param {String} header The new header
16872      */
16873     setColumnHeader : function(col, header){
16874         this.config[col].header = header;
16875         this.fireEvent("headerchange", this, col, header);
16876     },
16877
16878     /**
16879      * Returns the tooltip for the specified column.
16880      * @param {Number} col The column index
16881      * @return {String}
16882      */
16883     getColumnTooltip : function(col){
16884             return this.config[col].tooltip;
16885     },
16886     /**
16887      * Sets the tooltip for a column.
16888      * @param {Number} col The column index
16889      * @param {String} tooltip The new tooltip
16890      */
16891     setColumnTooltip : function(col, tooltip){
16892             this.config[col].tooltip = tooltip;
16893     },
16894
16895     /**
16896      * Returns the dataIndex for the specified column.
16897      * @param {Number} col The column index
16898      * @return {Number}
16899      */
16900     getDataIndex : function(col){
16901         return this.config[col].dataIndex;
16902     },
16903
16904     /**
16905      * Sets the dataIndex for a column.
16906      * @param {Number} col The column index
16907      * @param {Number} dataIndex The new dataIndex
16908      */
16909     setDataIndex : function(col, dataIndex){
16910         this.config[col].dataIndex = dataIndex;
16911     },
16912
16913     
16914     
16915     /**
16916      * Returns true if the cell is editable.
16917      * @param {Number} colIndex The column index
16918      * @param {Number} rowIndex The row index
16919      * @return {Boolean}
16920      */
16921     isCellEditable : function(colIndex, rowIndex){
16922         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16923     },
16924
16925     /**
16926      * Returns the editor defined for the cell/column.
16927      * return false or null to disable editing.
16928      * @param {Number} colIndex The column index
16929      * @param {Number} rowIndex The row index
16930      * @return {Object}
16931      */
16932     getCellEditor : function(colIndex, rowIndex){
16933         return this.config[colIndex].editor;
16934     },
16935
16936     /**
16937      * Sets if a column is editable.
16938      * @param {Number} col The column index
16939      * @param {Boolean} editable True if the column is editable
16940      */
16941     setEditable : function(col, editable){
16942         this.config[col].editable = editable;
16943     },
16944
16945
16946     /**
16947      * Returns true if the column is hidden.
16948      * @param {Number} colIndex The column index
16949      * @return {Boolean}
16950      */
16951     isHidden : function(colIndex){
16952         return this.config[colIndex].hidden;
16953     },
16954
16955
16956     /**
16957      * Returns true if the column width cannot be changed
16958      */
16959     isFixed : function(colIndex){
16960         return this.config[colIndex].fixed;
16961     },
16962
16963     /**
16964      * Returns true if the column can be resized
16965      * @return {Boolean}
16966      */
16967     isResizable : function(colIndex){
16968         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16969     },
16970     /**
16971      * Sets if a column is hidden.
16972      * @param {Number} colIndex The column index
16973      * @param {Boolean} hidden True if the column is hidden
16974      */
16975     setHidden : function(colIndex, hidden){
16976         this.config[colIndex].hidden = hidden;
16977         this.totalWidth = null;
16978         this.fireEvent("hiddenchange", this, colIndex, hidden);
16979     },
16980
16981     /**
16982      * Sets the editor for a column.
16983      * @param {Number} col The column index
16984      * @param {Object} editor The editor object
16985      */
16986     setEditor : function(col, editor){
16987         this.config[col].editor = editor;
16988     }
16989 });
16990
16991 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16992         if(typeof value == "string" && value.length < 1){
16993             return "&#160;";
16994         }
16995         return value;
16996 };
16997
16998 // Alias for backwards compatibility
16999 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
17000
17001 /**
17002  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17003  * @class Roo.bootstrap.Table.RowSelectionModel
17004  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17005  * It supports multiple selections and keyboard selection/navigation. 
17006  * @constructor
17007  * @param {Object} config
17008  */
17009
17010 Roo.bootstrap.Table.RowSelectionModel = function(config){
17011     Roo.apply(this, config);
17012     this.selections = new Roo.util.MixedCollection(false, function(o){
17013         return o.id;
17014     });
17015
17016     this.last = false;
17017     this.lastActive = false;
17018
17019     this.addEvents({
17020         /**
17021              * @event selectionchange
17022              * Fires when the selection changes
17023              * @param {SelectionModel} this
17024              */
17025             "selectionchange" : true,
17026         /**
17027              * @event afterselectionchange
17028              * Fires after the selection changes (eg. by key press or clicking)
17029              * @param {SelectionModel} this
17030              */
17031             "afterselectionchange" : true,
17032         /**
17033              * @event beforerowselect
17034              * Fires when a row is selected being selected, return false to cancel.
17035              * @param {SelectionModel} this
17036              * @param {Number} rowIndex The selected index
17037              * @param {Boolean} keepExisting False if other selections will be cleared
17038              */
17039             "beforerowselect" : true,
17040         /**
17041              * @event rowselect
17042              * Fires when a row is selected.
17043              * @param {SelectionModel} this
17044              * @param {Number} rowIndex The selected index
17045              * @param {Roo.data.Record} r The record
17046              */
17047             "rowselect" : true,
17048         /**
17049              * @event rowdeselect
17050              * Fires when a row is deselected.
17051              * @param {SelectionModel} this
17052              * @param {Number} rowIndex The selected index
17053              */
17054         "rowdeselect" : true
17055     });
17056     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17057     this.locked = false;
17058 };
17059
17060 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17061     /**
17062      * @cfg {Boolean} singleSelect
17063      * True to allow selection of only one row at a time (defaults to false)
17064      */
17065     singleSelect : false,
17066
17067     // private
17068     initEvents : function(){
17069
17070         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17071             this.grid.on("mousedown", this.handleMouseDown, this);
17072         }else{ // allow click to work like normal
17073             this.grid.on("rowclick", this.handleDragableRowClick, this);
17074         }
17075
17076         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17077             "up" : function(e){
17078                 if(!e.shiftKey){
17079                     this.selectPrevious(e.shiftKey);
17080                 }else if(this.last !== false && this.lastActive !== false){
17081                     var last = this.last;
17082                     this.selectRange(this.last,  this.lastActive-1);
17083                     this.grid.getView().focusRow(this.lastActive);
17084                     if(last !== false){
17085                         this.last = last;
17086                     }
17087                 }else{
17088                     this.selectFirstRow();
17089                 }
17090                 this.fireEvent("afterselectionchange", this);
17091             },
17092             "down" : function(e){
17093                 if(!e.shiftKey){
17094                     this.selectNext(e.shiftKey);
17095                 }else if(this.last !== false && this.lastActive !== false){
17096                     var last = this.last;
17097                     this.selectRange(this.last,  this.lastActive+1);
17098                     this.grid.getView().focusRow(this.lastActive);
17099                     if(last !== false){
17100                         this.last = last;
17101                     }
17102                 }else{
17103                     this.selectFirstRow();
17104                 }
17105                 this.fireEvent("afterselectionchange", this);
17106             },
17107             scope: this
17108         });
17109
17110         var view = this.grid.view;
17111         view.on("refresh", this.onRefresh, this);
17112         view.on("rowupdated", this.onRowUpdated, this);
17113         view.on("rowremoved", this.onRemove, this);
17114     },
17115
17116     // private
17117     onRefresh : function(){
17118         var ds = this.grid.dataSource, i, v = this.grid.view;
17119         var s = this.selections;
17120         s.each(function(r){
17121             if((i = ds.indexOfId(r.id)) != -1){
17122                 v.onRowSelect(i);
17123             }else{
17124                 s.remove(r);
17125             }
17126         });
17127     },
17128
17129     // private
17130     onRemove : function(v, index, r){
17131         this.selections.remove(r);
17132     },
17133
17134     // private
17135     onRowUpdated : function(v, index, r){
17136         if(this.isSelected(r)){
17137             v.onRowSelect(index);
17138         }
17139     },
17140
17141     /**
17142      * Select records.
17143      * @param {Array} records The records to select
17144      * @param {Boolean} keepExisting (optional) True to keep existing selections
17145      */
17146     selectRecords : function(records, keepExisting){
17147         if(!keepExisting){
17148             this.clearSelections();
17149         }
17150         var ds = this.grid.dataSource;
17151         for(var i = 0, len = records.length; i < len; i++){
17152             this.selectRow(ds.indexOf(records[i]), true);
17153         }
17154     },
17155
17156     /**
17157      * Gets the number of selected rows.
17158      * @return {Number}
17159      */
17160     getCount : function(){
17161         return this.selections.length;
17162     },
17163
17164     /**
17165      * Selects the first row in the grid.
17166      */
17167     selectFirstRow : function(){
17168         this.selectRow(0);
17169     },
17170
17171     /**
17172      * Select the last row.
17173      * @param {Boolean} keepExisting (optional) True to keep existing selections
17174      */
17175     selectLastRow : function(keepExisting){
17176         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17177     },
17178
17179     /**
17180      * Selects the row immediately following the last selected row.
17181      * @param {Boolean} keepExisting (optional) True to keep existing selections
17182      */
17183     selectNext : function(keepExisting){
17184         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17185             this.selectRow(this.last+1, keepExisting);
17186             this.grid.getView().focusRow(this.last);
17187         }
17188     },
17189
17190     /**
17191      * Selects the row that precedes the last selected row.
17192      * @param {Boolean} keepExisting (optional) True to keep existing selections
17193      */
17194     selectPrevious : function(keepExisting){
17195         if(this.last){
17196             this.selectRow(this.last-1, keepExisting);
17197             this.grid.getView().focusRow(this.last);
17198         }
17199     },
17200
17201     /**
17202      * Returns the selected records
17203      * @return {Array} Array of selected records
17204      */
17205     getSelections : function(){
17206         return [].concat(this.selections.items);
17207     },
17208
17209     /**
17210      * Returns the first selected record.
17211      * @return {Record}
17212      */
17213     getSelected : function(){
17214         return this.selections.itemAt(0);
17215     },
17216
17217
17218     /**
17219      * Clears all selections.
17220      */
17221     clearSelections : function(fast){
17222         if(this.locked) return;
17223         if(fast !== true){
17224             var ds = this.grid.dataSource;
17225             var s = this.selections;
17226             s.each(function(r){
17227                 this.deselectRow(ds.indexOfId(r.id));
17228             }, this);
17229             s.clear();
17230         }else{
17231             this.selections.clear();
17232         }
17233         this.last = false;
17234     },
17235
17236
17237     /**
17238      * Selects all rows.
17239      */
17240     selectAll : function(){
17241         if(this.locked) return;
17242         this.selections.clear();
17243         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17244             this.selectRow(i, true);
17245         }
17246     },
17247
17248     /**
17249      * Returns True if there is a selection.
17250      * @return {Boolean}
17251      */
17252     hasSelection : function(){
17253         return this.selections.length > 0;
17254     },
17255
17256     /**
17257      * Returns True if the specified row is selected.
17258      * @param {Number/Record} record The record or index of the record to check
17259      * @return {Boolean}
17260      */
17261     isSelected : function(index){
17262         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17263         return (r && this.selections.key(r.id) ? true : false);
17264     },
17265
17266     /**
17267      * Returns True if the specified record id is selected.
17268      * @param {String} id The id of record to check
17269      * @return {Boolean}
17270      */
17271     isIdSelected : function(id){
17272         return (this.selections.key(id) ? true : false);
17273     },
17274
17275     // private
17276     handleMouseDown : function(e, t){
17277         var view = this.grid.getView(), rowIndex;
17278         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17279             return;
17280         };
17281         if(e.shiftKey && this.last !== false){
17282             var last = this.last;
17283             this.selectRange(last, rowIndex, e.ctrlKey);
17284             this.last = last; // reset the last
17285             view.focusRow(rowIndex);
17286         }else{
17287             var isSelected = this.isSelected(rowIndex);
17288             if(e.button !== 0 && isSelected){
17289                 view.focusRow(rowIndex);
17290             }else if(e.ctrlKey && isSelected){
17291                 this.deselectRow(rowIndex);
17292             }else if(!isSelected){
17293                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17294                 view.focusRow(rowIndex);
17295             }
17296         }
17297         this.fireEvent("afterselectionchange", this);
17298     },
17299     // private
17300     handleDragableRowClick :  function(grid, rowIndex, e) 
17301     {
17302         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17303             this.selectRow(rowIndex, false);
17304             grid.view.focusRow(rowIndex);
17305              this.fireEvent("afterselectionchange", this);
17306         }
17307     },
17308     
17309     /**
17310      * Selects multiple rows.
17311      * @param {Array} rows Array of the indexes of the row to select
17312      * @param {Boolean} keepExisting (optional) True to keep existing selections
17313      */
17314     selectRows : function(rows, keepExisting){
17315         if(!keepExisting){
17316             this.clearSelections();
17317         }
17318         for(var i = 0, len = rows.length; i < len; i++){
17319             this.selectRow(rows[i], true);
17320         }
17321     },
17322
17323     /**
17324      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17325      * @param {Number} startRow The index of the first row in the range
17326      * @param {Number} endRow The index of the last row in the range
17327      * @param {Boolean} keepExisting (optional) True to retain existing selections
17328      */
17329     selectRange : function(startRow, endRow, keepExisting){
17330         if(this.locked) return;
17331         if(!keepExisting){
17332             this.clearSelections();
17333         }
17334         if(startRow <= endRow){
17335             for(var i = startRow; i <= endRow; i++){
17336                 this.selectRow(i, true);
17337             }
17338         }else{
17339             for(var i = startRow; i >= endRow; i--){
17340                 this.selectRow(i, true);
17341             }
17342         }
17343     },
17344
17345     /**
17346      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17347      * @param {Number} startRow The index of the first row in the range
17348      * @param {Number} endRow The index of the last row in the range
17349      */
17350     deselectRange : function(startRow, endRow, preventViewNotify){
17351         if(this.locked) return;
17352         for(var i = startRow; i <= endRow; i++){
17353             this.deselectRow(i, preventViewNotify);
17354         }
17355     },
17356
17357     /**
17358      * Selects a row.
17359      * @param {Number} row The index of the row to select
17360      * @param {Boolean} keepExisting (optional) True to keep existing selections
17361      */
17362     selectRow : function(index, keepExisting, preventViewNotify){
17363         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17364         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17365             if(!keepExisting || this.singleSelect){
17366                 this.clearSelections();
17367             }
17368             var r = this.grid.dataSource.getAt(index);
17369             this.selections.add(r);
17370             this.last = this.lastActive = index;
17371             if(!preventViewNotify){
17372                 this.grid.getView().onRowSelect(index);
17373             }
17374             this.fireEvent("rowselect", this, index, r);
17375             this.fireEvent("selectionchange", this);
17376         }
17377     },
17378
17379     /**
17380      * Deselects a row.
17381      * @param {Number} row The index of the row to deselect
17382      */
17383     deselectRow : function(index, preventViewNotify){
17384         if(this.locked) return;
17385         if(this.last == index){
17386             this.last = false;
17387         }
17388         if(this.lastActive == index){
17389             this.lastActive = false;
17390         }
17391         var r = this.grid.dataSource.getAt(index);
17392         this.selections.remove(r);
17393         if(!preventViewNotify){
17394             this.grid.getView().onRowDeselect(index);
17395         }
17396         this.fireEvent("rowdeselect", this, index);
17397         this.fireEvent("selectionchange", this);
17398     },
17399
17400     // private
17401     restoreLast : function(){
17402         if(this._last){
17403             this.last = this._last;
17404         }
17405     },
17406
17407     // private
17408     acceptsNav : function(row, col, cm){
17409         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17410     },
17411
17412     // private
17413     onEditorKey : function(field, e){
17414         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17415         if(k == e.TAB){
17416             e.stopEvent();
17417             ed.completeEdit();
17418             if(e.shiftKey){
17419                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17420             }else{
17421                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17422             }
17423         }else if(k == e.ENTER && !e.ctrlKey){
17424             e.stopEvent();
17425             ed.completeEdit();
17426             if(e.shiftKey){
17427                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17428             }else{
17429                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17430             }
17431         }else if(k == e.ESC){
17432             ed.cancelEdit();
17433         }
17434         if(newCell){
17435             g.startEditing(newCell[0], newCell[1]);
17436         }
17437     }
17438 });/*
17439  * - LGPL
17440  *
17441  * element
17442  * 
17443  */
17444
17445 /**
17446  * @class Roo.bootstrap.MessageBar
17447  * @extends Roo.bootstrap.Component
17448  * Bootstrap MessageBar class
17449  * @cfg {String} html contents of the MessageBar
17450  * @cfg {String} weight (info | success | warning | danger) default info
17451  * @cfg {String} beforeClass insert the bar before the given class
17452  * @cfg {Boolean} closable (true | false) default false
17453  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17454  * 
17455  * @constructor
17456  * Create a new Element
17457  * @param {Object} config The config object
17458  */
17459
17460 Roo.bootstrap.MessageBar = function(config){
17461     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17462 };
17463
17464 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17465     
17466     html: '',
17467     weight: 'info',
17468     closable: false,
17469     fixed: false,
17470     beforeClass: 'bootstrap-sticky-wrap',
17471     
17472     getAutoCreate : function(){
17473         
17474         var cfg = {
17475             tag: 'div',
17476             cls: 'alert alert-dismissable alert-' + this.weight,
17477             cn: [
17478                 {
17479                     tag: 'span',
17480                     cls: 'message',
17481                     html: this.html || ''
17482                 }
17483             ]
17484         }
17485         
17486         if(this.fixed){
17487             cfg.cls += ' alert-messages-fixed';
17488         }
17489         
17490         if(this.closable){
17491             cfg.cn.push({
17492                 tag: 'button',
17493                 cls: 'close',
17494                 html: 'x'
17495             });
17496         }
17497         
17498         return cfg;
17499     },
17500     
17501     onRender : function(ct, position)
17502     {
17503         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17504         
17505         if(!this.el){
17506             var cfg = Roo.apply({},  this.getAutoCreate());
17507             cfg.id = Roo.id();
17508             
17509             if (this.cls) {
17510                 cfg.cls += ' ' + this.cls;
17511             }
17512             if (this.style) {
17513                 cfg.style = this.style;
17514             }
17515             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17516             
17517             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17518         }
17519         
17520         this.el.select('>button.close').on('click', this.hide, this);
17521         
17522     },
17523     
17524     show : function()
17525     {
17526         if (!this.rendered) {
17527             this.render();
17528         }
17529         
17530         this.el.show();
17531         
17532         this.fireEvent('show', this);
17533         
17534     },
17535     
17536     hide : function()
17537     {
17538         if (!this.rendered) {
17539             this.render();
17540         }
17541         
17542         this.el.hide();
17543         
17544         this.fireEvent('hide', this);
17545     },
17546     
17547     update : function()
17548     {
17549 //        var e = this.el.dom.firstChild;
17550 //        
17551 //        if(this.closable){
17552 //            e = e.nextSibling;
17553 //        }
17554 //        
17555 //        e.data = this.html || '';
17556
17557         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17558     }
17559    
17560 });
17561
17562  
17563
17564