roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
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          skip_children = false;
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](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 
214                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
215                 // and are not displayed -this causes this to use up the wrong element when matching.
216                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
217                 
218                 
219                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
220                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
221                   
222                   
223                   
224                     cn.el = echild;
225                   //  Roo.log("GOT");
226                     //echild.dom.removeAttribute('xtype');
227                 } else {
228                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229                     Roo.log(self_cntr_el);
230                     Roo.log(echild);
231                     Roo.log(cn);
232                 }
233             }
234            
235             
236            
237             // if object has flexy:if - then it may or may not be rendered.
238             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
239                 // skip a flexy if element.
240                 Roo.log('skipping render');
241                 Roo.log(tree);
242                 if (!cn.el) {
243                     Roo.log('skipping all children');
244                     skip_children = true;
245                 }
246                 
247              } else {
248                  
249                 // actually if flexy:foreach is found, we really want to create 
250                 // multiple copies here...
251                 //Roo.log('render');
252                 //Roo.log(this[cntr]());
253                 cn.render(this[cntr](true));
254              }
255             // then add the element..
256         }
257         
258         
259         // handle the kids..
260         
261         var nitems = [];
262         /*
263         if (typeof (tree.menu) != 'undefined') {
264             tree.menu.parentType = cn.xtype;
265             tree.menu.triggerEl = cn.el;
266             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
267             
268         }
269         */
270         if (!tree.items || !tree.items.length) {
271             cn.items = nitems;
272             return cn;
273         }
274         var items = tree.items;
275         delete tree.items;
276         
277         //Roo.log(items.length);
278             // add the items..
279         if (!skip_children) {    
280             for(var i =0;i < items.length;i++) {
281                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
282             }
283         }
284         
285         cn.items = nitems;
286         
287         return cn;
288     }
289     
290     
291     
292     
293 });
294
295  /*
296  * - LGPL
297  *
298  * Body
299  * 
300  */
301
302 /**
303  * @class Roo.bootstrap.Body
304  * @extends Roo.bootstrap.Component
305  * Bootstrap Body class
306  * 
307  * @constructor
308  * Create a new body
309  * @param {Object} config The config object
310  */
311
312 Roo.bootstrap.Body = function(config){
313     Roo.bootstrap.Body.superclass.constructor.call(this, config);
314     this.el = Roo.get(document.body);
315     if (this.cls && this.cls.length) {
316         Roo.get(document.body).addClass(this.cls);
317     }
318 };
319
320 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
321       
322         autoCreate : {
323         cls: 'container'
324     },
325     onRender : function(ct, position)
326     {
327        /* Roo.log("Roo.bootstrap.Body - onRender");
328         if (this.cls && this.cls.length) {
329             Roo.get(document.body).addClass(this.cls);
330         }
331         // style??? xttr???
332         */
333     }
334     
335     
336  
337    
338 });
339
340  /*
341  * - LGPL
342  *
343  * button group
344  * 
345  */
346
347
348 /**
349  * @class Roo.bootstrap.ButtonGroup
350  * @extends Roo.bootstrap.Component
351  * Bootstrap ButtonGroup class
352  * @cfg {String} size lg | sm | xs (default empty normal)
353  * @cfg {String} align vertical | justified  (default none)
354  * @cfg {String} direction up | down (default down)
355  * @cfg {Boolean} toolbar false | true
356  * @cfg {Boolean} btn true | false
357  * 
358  * 
359  * @constructor
360  * Create a new Input
361  * @param {Object} config The config object
362  */
363
364 Roo.bootstrap.ButtonGroup = function(config){
365     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
366 };
367
368 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
369     
370     size: '',
371     align: '',
372     direction: '',
373     toolbar: false,
374     btn: true,
375
376     getAutoCreate : function(){
377         var cfg = {
378             cls: 'btn-group',
379             html : null
380         }
381         
382         cfg.html = this.html || cfg.html;
383         
384         if (this.toolbar) {
385             cfg = {
386                 cls: 'btn-toolbar',
387                 html: null
388             }
389             
390             return cfg;
391         }
392         
393         if (['vertical','justified'].indexOf(this.align)!==-1) {
394             cfg.cls = 'btn-group-' + this.align;
395             
396             if (this.align == 'justified') {
397                 console.log(this.items);
398             }
399         }
400         
401         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
402             cfg.cls += ' btn-group-' + this.size;
403         }
404         
405         if (this.direction == 'up') {
406             cfg.cls += ' dropup' ;
407         }
408         
409         return cfg;
410     }
411    
412 });
413
414  /*
415  * - LGPL
416  *
417  * button
418  * 
419  */
420
421 /**
422  * @class Roo.bootstrap.Button
423  * @extends Roo.bootstrap.Component
424  * Bootstrap Button class
425  * @cfg {String} html The button content
426  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
427  * @cfg {String} size empty | lg | sm | xs
428  * @cfg {String} tag empty | a | input | submit
429  * @cfg {String} href empty or href
430  * @cfg {Boolean} disabled false | true
431  * @cfg {Boolean} isClose false | true
432  * @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
433  * @cfg {String} badge text for badge
434  * @cfg {String} theme default (or empty) | glow
435  * @cfg {Boolean} inverse false | true
436  * @cfg {Boolean} toggle false | true
437  * @cfg {String} ontext text for on toggle state
438  * @cfg {String} offtext text for off toggle state
439  * @cfg {Boolean} defaulton true | false
440  * @cfg {Boolean} preventDefault (true | false) default true
441  * @cfg {Boolean} removeClass true | false remove the standard class..
442  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
443  * 
444  * @constructor
445  * Create a new button
446  * @param {Object} config The config object
447  */
448
449
450 Roo.bootstrap.Button = function(config){
451     Roo.bootstrap.Button.superclass.constructor.call(this, config);
452     this.addEvents({
453         // raw events
454         /**
455          * @event click
456          * When a butotn is pressed
457          * @param {Roo.EventObject} e
458          */
459         "click" : true,
460          /**
461          * @event toggle
462          * After the button has been toggles
463          * @param {Roo.EventObject} e
464          * @param {boolean} pressed (also available as button.pressed)
465          */
466         "toggle" : true
467     });
468 };
469
470 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
471     html: false,
472     active: false,
473     weight: '',
474     size: '',
475     tag: 'button',
476     href: '',
477     disabled: false,
478     isClose: false,
479     glyphicon: '',
480     badge: '',
481     theme: 'default',
482     inverse: false,
483     
484     toggle: false,
485     ontext: 'ON',
486     offtext: 'OFF',
487     defaulton: true,
488     preventDefault: true,
489     removeClass: false,
490     name: false,
491     target: false,
492     
493     
494     pressed : null,
495      
496     
497     getAutoCreate : function(){
498         
499         var cfg = {
500             tag : 'button',
501             cls : 'roo-button',
502             html: ''
503         };
504         
505         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
506             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
507             this.tag = 'button';
508         } else {
509             cfg.tag = this.tag;
510         }
511         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
512         
513         if (this.toggle == true) {
514             cfg={
515                 tag: 'div',
516                 cls: 'slider-frame roo-button',
517                 cn: [
518                     {
519                         tag: 'span',
520                         'data-on-text':'ON',
521                         'data-off-text':'OFF',
522                         cls: 'slider-button',
523                         html: this.offtext
524                     }
525                 ]
526             };
527             
528             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
529                 cfg.cls += ' '+this.weight;
530             }
531             
532             return cfg;
533         }
534         
535         if (this.isClose) {
536             cfg.cls += ' close';
537             
538             cfg["aria-hidden"] = true;
539             
540             cfg.html = "&times;";
541             
542             return cfg;
543         }
544         
545          
546         if (this.theme==='default') {
547             cfg.cls = 'btn roo-button';
548             
549             //if (this.parentType != 'Navbar') {
550             this.weight = this.weight.length ?  this.weight : 'default';
551             //}
552             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
553                 
554                 cfg.cls += ' btn-' + this.weight;
555             }
556         } else if (this.theme==='glow') {
557             
558             cfg.tag = 'a';
559             cfg.cls = 'btn-glow roo-button';
560             
561             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
562                 
563                 cfg.cls += ' ' + this.weight;
564             }
565         }
566    
567         
568         if (this.inverse) {
569             this.cls += ' inverse';
570         }
571         
572         
573         if (this.active) {
574             cfg.cls += ' active';
575         }
576         
577         if (this.disabled) {
578             cfg.disabled = 'disabled';
579         }
580         
581         if (this.items) {
582             Roo.log('changing to ul' );
583             cfg.tag = 'ul';
584             this.glyphicon = 'caret';
585         }
586         
587         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
588          
589         //gsRoo.log(this.parentType);
590         if (this.parentType === 'Navbar' && !this.parent().bar) {
591             Roo.log('changing to li?');
592             
593             cfg.tag = 'li';
594             
595             cfg.cls = '';
596             cfg.cn =  [{
597                 tag : 'a',
598                 cls : 'roo-button',
599                 html : this.html,
600                 href : this.href || '#'
601             }];
602             if (this.menu) {
603                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
604                 cfg.cls += ' dropdown';
605             }   
606             
607             delete cfg.html;
608             
609         }
610         
611        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
612         
613         if (this.glyphicon) {
614             cfg.html = ' ' + cfg.html;
615             
616             cfg.cn = [
617                 {
618                     tag: 'span',
619                     cls: 'glyphicon glyphicon-' + this.glyphicon
620                 }
621             ];
622         }
623         
624         if (this.badge) {
625             cfg.html += ' ';
626             
627             cfg.tag = 'a';
628             
629 //            cfg.cls='btn roo-button';
630             
631             cfg.href=this.href;
632             
633             var value = cfg.html;
634             
635             if(this.glyphicon){
636                 value = {
637                             tag: 'span',
638                             cls: 'glyphicon glyphicon-' + this.glyphicon,
639                             html: this.html
640                         };
641                 
642             }
643             
644             cfg.cn = [
645                 value,
646                 {
647                     tag: 'span',
648                     cls: 'badge',
649                     html: this.badge
650                 }
651             ];
652             
653             cfg.html='';
654         }
655         
656         if (this.menu) {
657             cfg.cls += ' dropdown';
658             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
659         }
660         
661         if (cfg.tag !== 'a' && this.href !== '') {
662             throw "Tag must be a to set href.";
663         } else if (this.href.length > 0) {
664             cfg.href = this.href;
665         }
666         
667         if(this.removeClass){
668             cfg.cls = '';
669         }
670         
671         if(this.target){
672             cfg.target = this.target;
673         }
674         
675         return cfg;
676     },
677     initEvents: function() {
678        // Roo.log('init events?');
679 //        Roo.log(this.el.dom);
680         // add the menu...
681         
682         if (typeof (this.menu) != 'undefined') {
683             this.menu.parentType = this.xtype;
684             this.menu.triggerEl = this.el;
685             this.addxtype(Roo.apply({}, this.menu));
686         }
687
688
689        if (this.el.hasClass('roo-button')) {
690             this.el.on('click', this.onClick, this);
691        } else {
692             this.el.select('.roo-button').on('click', this.onClick, this);
693        }
694        
695        if(this.removeClass){
696            this.el.on('click', this.onClick, this);
697        }
698        
699        this.el.enableDisplayMode();
700         
701     },
702     onClick : function(e)
703     {
704         if (this.disabled) {
705             return;
706         }
707         
708         Roo.log('button on click ');
709         if(this.preventDefault){
710             e.preventDefault();
711         }
712         if (this.pressed === true || this.pressed === false) {
713             this.pressed = !this.pressed;
714             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
715             this.fireEvent('toggle', this, e, this.pressed);
716         }
717         
718         
719         this.fireEvent('click', this, e);
720     },
721     
722     /**
723      * Enables this button
724      */
725     enable : function()
726     {
727         this.disabled = false;
728         this.el.removeClass('disabled');
729     },
730     
731     /**
732      * Disable this button
733      */
734     disable : function()
735     {
736         this.disabled = true;
737         this.el.addClass('disabled');
738     },
739      /**
740      * sets the active state on/off, 
741      * @param {Boolean} state (optional) Force a particular state
742      */
743     setActive : function(v) {
744         
745         this.el[v ? 'addClass' : 'removeClass']('active');
746     },
747      /**
748      * toggles the current active state 
749      */
750     toggleActive : function()
751     {
752        var active = this.el.hasClass('active');
753        this.setActive(!active);
754        
755         
756     },
757     setText : function(str)
758     {
759         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
760     },
761     getText : function()
762     {
763         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
764     },
765     hide: function() {
766        
767      
768         this.el.hide();   
769     },
770     show: function() {
771        
772         this.el.show();   
773     }
774     
775     
776 });
777
778  /*
779  * - LGPL
780  *
781  * column
782  * 
783  */
784
785 /**
786  * @class Roo.bootstrap.Column
787  * @extends Roo.bootstrap.Component
788  * Bootstrap Column class
789  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
790  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
791  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
792  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
793  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
794  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
795  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
796  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
797  *
798  * 
799  * @cfg {Boolean} hidden (true|false) hide the element
800  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
801  * @cfg {String} fa (ban|check|...) font awesome icon
802  * @cfg {Number} fasize (1|2|....) font awsome size
803
804  * @cfg {String} icon (info-sign|check|...) glyphicon name
805
806  * @cfg {String} html content of column.
807  * 
808  * @constructor
809  * Create a new Column
810  * @param {Object} config The config object
811  */
812
813 Roo.bootstrap.Column = function(config){
814     Roo.bootstrap.Column.superclass.constructor.call(this, config);
815 };
816
817 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
818     
819     xs: false,
820     sm: false,
821     md: false,
822     lg: false,
823     xsoff: false,
824     smoff: false,
825     mdoff: false,
826     lgoff: false,
827     html: '',
828     offset: 0,
829     alert: false,
830     fa: false,
831     icon : false,
832     hidden : false,
833     fasize : 1,
834     
835     getAutoCreate : function(){
836         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
837         
838         cfg = {
839             tag: 'div',
840             cls: 'column'
841         };
842         
843         var settings=this;
844         ['xs','sm','md','lg'].map(function(size){
845             //Roo.log( size + ':' + settings[size]);
846             
847             if (settings[size+'off'] !== false) {
848                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
849             }
850             
851             if (settings[size] === false) {
852                 return;
853             }
854             Roo.log(settings[size]);
855             if (!settings[size]) { // 0 = hidden
856                 cfg.cls += ' hidden-' + size;
857                 return;
858             }
859             cfg.cls += ' col-' + size + '-' + settings[size];
860             
861         });
862         
863         if (this.hidden) {
864             cfg.cls += ' hidden';
865         }
866         
867         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
868             cfg.cls +=' alert alert-' + this.alert;
869         }
870         
871         
872         if (this.html.length) {
873             cfg.html = this.html;
874         }
875         if (this.fa) {
876             var fasize = '';
877             if (this.fasize > 1) {
878                 fasize = ' fa-' + this.fasize + 'x';
879             }
880             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
881             
882             
883         }
884         if (this.icon) {
885             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
886         }
887         
888         return cfg;
889     }
890    
891 });
892
893  
894
895  /*
896  * - LGPL
897  *
898  * page container.
899  * 
900  */
901
902
903 /**
904  * @class Roo.bootstrap.Container
905  * @extends Roo.bootstrap.Component
906  * Bootstrap Container class
907  * @cfg {Boolean} jumbotron is it a jumbotron element
908  * @cfg {String} html content of element
909  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
910  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
911  * @cfg {String} header content of header (for panel)
912  * @cfg {String} footer content of footer (for panel)
913  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
914  * @cfg {String} tag (header|aside|section) type of HTML tag.
915  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
916  * @cfg {String} fa (ban|check|...) font awesome icon
917  * @cfg {String} icon (info-sign|check|...) glyphicon name
918  * @cfg {Boolean} hidden (true|false) hide the element
919
920  *     
921  * @constructor
922  * Create a new Container
923  * @param {Object} config The config object
924  */
925
926 Roo.bootstrap.Container = function(config){
927     Roo.bootstrap.Container.superclass.constructor.call(this, config);
928 };
929
930 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
931     
932     jumbotron : false,
933     well: '',
934     panel : '',
935     header: '',
936     footer : '',
937     sticky: '',
938     tag : false,
939     alert : false,
940     fa: false,
941     icon : false,
942   
943      
944     getChildContainer : function() {
945         
946         if(!this.el){
947             return false;
948         }
949         
950         if (this.panel.length) {
951             return this.el.select('.panel-body',true).first();
952         }
953         
954         return this.el;
955     },
956     
957     
958     getAutoCreate : function(){
959         
960         var cfg = {
961             tag : this.tag || 'div',
962             html : '',
963             cls : ''
964         };
965         if (this.jumbotron) {
966             cfg.cls = 'jumbotron';
967         }
968         
969         
970         
971         // - this is applied by the parent..
972         //if (this.cls) {
973         //    cfg.cls = this.cls + '';
974         //}
975         
976         if (this.sticky.length) {
977             
978             var bd = Roo.get(document.body);
979             if (!bd.hasClass('bootstrap-sticky')) {
980                 bd.addClass('bootstrap-sticky');
981                 Roo.select('html',true).setStyle('height', '100%');
982             }
983              
984             cfg.cls += 'bootstrap-sticky-' + this.sticky;
985         }
986         
987         
988         if (this.well.length) {
989             switch (this.well) {
990                 case 'lg':
991                 case 'sm':
992                     cfg.cls +=' well well-' +this.well;
993                     break;
994                 default:
995                     cfg.cls +=' well';
996                     break;
997             }
998         }
999         
1000         if (this.hidden) {
1001             cfg.cls += ' hidden';
1002         }
1003         
1004         
1005         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1006             cfg.cls +=' alert alert-' + this.alert;
1007         }
1008         
1009         var body = cfg;
1010         
1011         if (this.panel.length) {
1012             cfg.cls += ' panel panel-' + this.panel;
1013             cfg.cn = [];
1014             if (this.header.length) {
1015                 cfg.cn.push({
1016                     
1017                     cls : 'panel-heading',
1018                     cn : [{
1019                         tag: 'h3',
1020                         cls : 'panel-title',
1021                         html : this.header
1022                     }]
1023                     
1024                 });
1025             }
1026             body = false;
1027             cfg.cn.push({
1028                 cls : 'panel-body',
1029                 html : this.html
1030             });
1031             
1032             
1033             if (this.footer.length) {
1034                 cfg.cn.push({
1035                     cls : 'panel-footer',
1036                     html : this.footer
1037                     
1038                 });
1039             }
1040             
1041         }
1042         
1043         if (body) {
1044             body.html = this.html || cfg.html;
1045             // prefix with the icons..
1046             if (this.fa) {
1047                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1048             }
1049             if (this.icon) {
1050                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1051             }
1052             
1053             
1054         }
1055         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1056             cfg.cls =  'container';
1057         }
1058         
1059         return cfg;
1060     },
1061     
1062     titleEl : function()
1063     {
1064         if(!this.el || !this.panel.length || !this.header.length){
1065             return;
1066         }
1067         
1068         return this.el.select('.panel-title',true).first();
1069     },
1070     
1071     setTitle : function(v)
1072     {
1073         var titleEl = this.titleEl();
1074         
1075         if(!titleEl){
1076             return;
1077         }
1078         
1079         titleEl.dom.innerHTML = v;
1080     },
1081     
1082     getTitle : function()
1083     {
1084         
1085         var titleEl = this.titleEl();
1086         
1087         if(!titleEl){
1088             return '';
1089         }
1090         
1091         return titleEl.dom.innerHTML;
1092     }
1093    
1094 });
1095
1096  /*
1097  * - LGPL
1098  *
1099  * image
1100  * 
1101  */
1102
1103
1104 /**
1105  * @class Roo.bootstrap.Img
1106  * @extends Roo.bootstrap.Component
1107  * Bootstrap Img class
1108  * @cfg {Boolean} imgResponsive false | true
1109  * @cfg {String} border rounded | circle | thumbnail
1110  * @cfg {String} src image source
1111  * @cfg {String} alt image alternative text
1112  * @cfg {String} href a tag href
1113  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1114  * 
1115  * @constructor
1116  * Create a new Input
1117  * @param {Object} config The config object
1118  */
1119
1120 Roo.bootstrap.Img = function(config){
1121     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1122     
1123     this.addEvents({
1124         // img events
1125         /**
1126          * @event click
1127          * The img click event for the img.
1128          * @param {Roo.EventObject} e
1129          */
1130         "click" : true
1131     });
1132 };
1133
1134 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1135     
1136     imgResponsive: true,
1137     border: '',
1138     src: '',
1139     href: false,
1140     target: false,
1141
1142     getAutoCreate : function(){
1143         
1144         var cfg = {
1145             tag: 'img',
1146             cls: (this.imgResponsive) ? 'img-responsive' : '',
1147             html : null
1148         }
1149         
1150         cfg.html = this.html || cfg.html;
1151         
1152         cfg.src = this.src || cfg.src;
1153         
1154         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1155             cfg.cls += ' img-' + this.border;
1156         }
1157         
1158         if(this.alt){
1159             cfg.alt = this.alt;
1160         }
1161         
1162         if(this.href){
1163             var a = {
1164                 tag: 'a',
1165                 href: this.href,
1166                 cn: [
1167                     cfg
1168                 ]
1169             }
1170             
1171             if(this.target){
1172                 a.target = this.target;
1173             }
1174             
1175         }
1176         
1177         
1178         return (this.href) ? a : cfg;
1179     },
1180     
1181     initEvents: function() {
1182         
1183         if(!this.href){
1184             this.el.on('click', this.onClick, this);
1185         }
1186     },
1187     
1188     onClick : function(e)
1189     {
1190         Roo.log('img onclick');
1191         this.fireEvent('click', this, e);
1192     }
1193    
1194 });
1195
1196  /*
1197  * - LGPL
1198  *
1199  * image
1200  * 
1201  */
1202
1203
1204 /**
1205  * @class Roo.bootstrap.Link
1206  * @extends Roo.bootstrap.Component
1207  * Bootstrap Link Class
1208  * @cfg {String} alt image alternative text
1209  * @cfg {String} href a tag href
1210  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1211  * @cfg {String} html the content of the link.
1212  * @cfg {String} anchor name for the anchor link
1213
1214  * @cfg {Boolean} preventDefault (true | false) default false
1215
1216  * 
1217  * @constructor
1218  * Create a new Input
1219  * @param {Object} config The config object
1220  */
1221
1222 Roo.bootstrap.Link = function(config){
1223     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1224     
1225     this.addEvents({
1226         // img events
1227         /**
1228          * @event click
1229          * The img click event for the img.
1230          * @param {Roo.EventObject} e
1231          */
1232         "click" : true
1233     });
1234 };
1235
1236 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1237     
1238     href: false,
1239     target: false,
1240     preventDefault: false,
1241     anchor : false,
1242     alt : false,
1243
1244     getAutoCreate : function()
1245     {
1246         
1247         var cfg = {
1248             tag: 'a'
1249         };
1250         // anchor's do not require html/href...
1251         if (this.anchor === false) {
1252             cfg.html = this.html || 'html-missing';
1253             cfg.href = this.href || '#';
1254         } else {
1255             cfg.name = this.anchor;
1256             if (this.html !== false) {
1257                 cfg.html = this.html;
1258             }
1259             if (this.href !== false) {
1260                 cfg.href = this.href;
1261             }
1262         }
1263         
1264         if(this.alt !== false){
1265             cfg.alt = this.alt;
1266         }
1267         
1268         
1269         if(this.target !== false) {
1270             cfg.target = this.target;
1271         }
1272         
1273         return cfg;
1274     },
1275     
1276     initEvents: function() {
1277         
1278         if(!this.href || this.preventDefault){
1279             this.el.on('click', this.onClick, this);
1280         }
1281     },
1282     
1283     onClick : function(e)
1284     {
1285         if(this.preventDefault){
1286             e.preventDefault();
1287         }
1288         //Roo.log('img onclick');
1289         this.fireEvent('click', this, e);
1290     }
1291    
1292 });
1293
1294  /*
1295  * - LGPL
1296  *
1297  * header
1298  * 
1299  */
1300
1301 /**
1302  * @class Roo.bootstrap.Header
1303  * @extends Roo.bootstrap.Component
1304  * Bootstrap Header class
1305  * @cfg {String} html content of header
1306  * @cfg {Number} level (1|2|3|4|5|6) default 1
1307  * 
1308  * @constructor
1309  * Create a new Header
1310  * @param {Object} config The config object
1311  */
1312
1313
1314 Roo.bootstrap.Header  = function(config){
1315     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1316 };
1317
1318 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1319     
1320     //href : false,
1321     html : false,
1322     level : 1,
1323     
1324     
1325     
1326     getAutoCreate : function(){
1327         
1328         var cfg = {
1329             tag: 'h' + (1 *this.level),
1330             html: this.html || 'fill in html'
1331         } ;
1332         
1333         return cfg;
1334     }
1335    
1336 });
1337
1338  
1339
1340  /*
1341  * Based on:
1342  * Ext JS Library 1.1.1
1343  * Copyright(c) 2006-2007, Ext JS, LLC.
1344  *
1345  * Originally Released Under LGPL - original licence link has changed is not relivant.
1346  *
1347  * Fork - LGPL
1348  * <script type="text/javascript">
1349  */
1350  
1351 /**
1352  * @class Roo.bootstrap.MenuMgr
1353  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1354  * @singleton
1355  */
1356 Roo.bootstrap.MenuMgr = function(){
1357    var menus, active, groups = {}, attached = false, lastShow = new Date();
1358
1359    // private - called when first menu is created
1360    function init(){
1361        menus = {};
1362        active = new Roo.util.MixedCollection();
1363        Roo.get(document).addKeyListener(27, function(){
1364            if(active.length > 0){
1365                hideAll();
1366            }
1367        });
1368    }
1369
1370    // private
1371    function hideAll(){
1372        if(active && active.length > 0){
1373            var c = active.clone();
1374            c.each(function(m){
1375                m.hide();
1376            });
1377        }
1378    }
1379
1380    // private
1381    function onHide(m){
1382        active.remove(m);
1383        if(active.length < 1){
1384            Roo.get(document).un("mouseup", onMouseDown);
1385             
1386            attached = false;
1387        }
1388    }
1389
1390    // private
1391    function onShow(m){
1392        var last = active.last();
1393        lastShow = new Date();
1394        active.add(m);
1395        if(!attached){
1396           Roo.get(document).on("mouseup", onMouseDown);
1397            
1398            attached = true;
1399        }
1400        if(m.parentMenu){
1401           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1402           m.parentMenu.activeChild = m;
1403        }else if(last && last.isVisible()){
1404           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1405        }
1406    }
1407
1408    // private
1409    function onBeforeHide(m){
1410        if(m.activeChild){
1411            m.activeChild.hide();
1412        }
1413        if(m.autoHideTimer){
1414            clearTimeout(m.autoHideTimer);
1415            delete m.autoHideTimer;
1416        }
1417    }
1418
1419    // private
1420    function onBeforeShow(m){
1421        var pm = m.parentMenu;
1422        if(!pm && !m.allowOtherMenus){
1423            hideAll();
1424        }else if(pm && pm.activeChild && active != m){
1425            pm.activeChild.hide();
1426        }
1427    }
1428
1429    // private
1430    function onMouseDown(e){
1431         Roo.log("on MouseDown");
1432         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1433            hideAll();
1434         }
1435         
1436         
1437    }
1438
1439    // private
1440    function onBeforeCheck(mi, state){
1441        if(state){
1442            var g = groups[mi.group];
1443            for(var i = 0, l = g.length; i < l; i++){
1444                if(g[i] != mi){
1445                    g[i].setChecked(false);
1446                }
1447            }
1448        }
1449    }
1450
1451    return {
1452
1453        /**
1454         * Hides all menus that are currently visible
1455         */
1456        hideAll : function(){
1457             hideAll();  
1458        },
1459
1460        // private
1461        register : function(menu){
1462            if(!menus){
1463                init();
1464            }
1465            menus[menu.id] = menu;
1466            menu.on("beforehide", onBeforeHide);
1467            menu.on("hide", onHide);
1468            menu.on("beforeshow", onBeforeShow);
1469            menu.on("show", onShow);
1470            var g = menu.group;
1471            if(g && menu.events["checkchange"]){
1472                if(!groups[g]){
1473                    groups[g] = [];
1474                }
1475                groups[g].push(menu);
1476                menu.on("checkchange", onCheck);
1477            }
1478        },
1479
1480         /**
1481          * Returns a {@link Roo.menu.Menu} object
1482          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1483          * be used to generate and return a new Menu instance.
1484          */
1485        get : function(menu){
1486            if(typeof menu == "string"){ // menu id
1487                return menus[menu];
1488            }else if(menu.events){  // menu instance
1489                return menu;
1490            }
1491            /*else if(typeof menu.length == 'number'){ // array of menu items?
1492                return new Roo.bootstrap.Menu({items:menu});
1493            }else{ // otherwise, must be a config
1494                return new Roo.bootstrap.Menu(menu);
1495            }
1496            */
1497            return false;
1498        },
1499
1500        // private
1501        unregister : function(menu){
1502            delete menus[menu.id];
1503            menu.un("beforehide", onBeforeHide);
1504            menu.un("hide", onHide);
1505            menu.un("beforeshow", onBeforeShow);
1506            menu.un("show", onShow);
1507            var g = menu.group;
1508            if(g && menu.events["checkchange"]){
1509                groups[g].remove(menu);
1510                menu.un("checkchange", onCheck);
1511            }
1512        },
1513
1514        // private
1515        registerCheckable : function(menuItem){
1516            var g = menuItem.group;
1517            if(g){
1518                if(!groups[g]){
1519                    groups[g] = [];
1520                }
1521                groups[g].push(menuItem);
1522                menuItem.on("beforecheckchange", onBeforeCheck);
1523            }
1524        },
1525
1526        // private
1527        unregisterCheckable : function(menuItem){
1528            var g = menuItem.group;
1529            if(g){
1530                groups[g].remove(menuItem);
1531                menuItem.un("beforecheckchange", onBeforeCheck);
1532            }
1533        }
1534    };
1535 }();/*
1536  * - LGPL
1537  *
1538  * menu
1539  * 
1540  */
1541
1542 /**
1543  * @class Roo.bootstrap.Menu
1544  * @extends Roo.bootstrap.Component
1545  * Bootstrap Menu class - container for MenuItems
1546  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1547  * 
1548  * @constructor
1549  * Create a new Menu
1550  * @param {Object} config The config object
1551  */
1552
1553
1554 Roo.bootstrap.Menu = function(config){
1555     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1556     if (this.registerMenu) {
1557         Roo.bootstrap.MenuMgr.register(this);
1558     }
1559     this.addEvents({
1560         /**
1561          * @event beforeshow
1562          * Fires before this menu is displayed
1563          * @param {Roo.menu.Menu} this
1564          */
1565         beforeshow : true,
1566         /**
1567          * @event beforehide
1568          * Fires before this menu is hidden
1569          * @param {Roo.menu.Menu} this
1570          */
1571         beforehide : true,
1572         /**
1573          * @event show
1574          * Fires after this menu is displayed
1575          * @param {Roo.menu.Menu} this
1576          */
1577         show : true,
1578         /**
1579          * @event hide
1580          * Fires after this menu is hidden
1581          * @param {Roo.menu.Menu} this
1582          */
1583         hide : true,
1584         /**
1585          * @event click
1586          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1587          * @param {Roo.menu.Menu} this
1588          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1589          * @param {Roo.EventObject} e
1590          */
1591         click : true,
1592         /**
1593          * @event mouseover
1594          * Fires when the mouse is hovering over this menu
1595          * @param {Roo.menu.Menu} this
1596          * @param {Roo.EventObject} e
1597          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1598          */
1599         mouseover : true,
1600         /**
1601          * @event mouseout
1602          * Fires when the mouse exits this menu
1603          * @param {Roo.menu.Menu} this
1604          * @param {Roo.EventObject} e
1605          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1606          */
1607         mouseout : true,
1608         /**
1609          * @event itemclick
1610          * Fires when a menu item contained in this menu is clicked
1611          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1612          * @param {Roo.EventObject} e
1613          */
1614         itemclick: true
1615     });
1616     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1617 };
1618
1619 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1620     
1621    /// html : false,
1622     //align : '',
1623     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1624     type: false,
1625     /**
1626      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1627      */
1628     registerMenu : true,
1629     
1630     menuItems :false, // stores the menu items..
1631     
1632     hidden:true,
1633     
1634     parentMenu : false,
1635     
1636     getChildContainer : function() {
1637         return this.el;  
1638     },
1639     
1640     getAutoCreate : function(){
1641          
1642         //if (['right'].indexOf(this.align)!==-1) {
1643         //    cfg.cn[1].cls += ' pull-right'
1644         //}
1645         
1646         
1647         var cfg = {
1648             tag : 'ul',
1649             cls : 'dropdown-menu' ,
1650             style : 'z-index:1000'
1651             
1652         }
1653         
1654         if (this.type === 'submenu') {
1655             cfg.cls = 'submenu active';
1656         }
1657         if (this.type === 'treeview') {
1658             cfg.cls = 'treeview-menu';
1659         }
1660         
1661         return cfg;
1662     },
1663     initEvents : function() {
1664         
1665        // Roo.log("ADD event");
1666        // Roo.log(this.triggerEl.dom);
1667         this.triggerEl.on('click', this.onTriggerPress, this);
1668         this.triggerEl.addClass('dropdown-toggle');
1669         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1670
1671         this.el.on("mouseover", this.onMouseOver, this);
1672         this.el.on("mouseout", this.onMouseOut, this);
1673         
1674         
1675     },
1676     findTargetItem : function(e){
1677         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1678         if(!t){
1679             return false;
1680         }
1681         //Roo.log(t);         Roo.log(t.id);
1682         if(t && t.id){
1683             //Roo.log(this.menuitems);
1684             return this.menuitems.get(t.id);
1685             
1686             //return this.items.get(t.menuItemId);
1687         }
1688         
1689         return false;
1690     },
1691     onClick : function(e){
1692         Roo.log("menu.onClick");
1693         var t = this.findTargetItem(e);
1694         if(!t || t.isContainer){
1695             return;
1696         }
1697         Roo.log(e);
1698         /*
1699         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1700             if(t == this.activeItem && t.shouldDeactivate(e)){
1701                 this.activeItem.deactivate();
1702                 delete this.activeItem;
1703                 return;
1704             }
1705             if(t.canActivate){
1706                 this.setActiveItem(t, true);
1707             }
1708             return;
1709             
1710             
1711         }
1712         */
1713        
1714         Roo.log('pass click event');
1715         
1716         t.onClick(e);
1717         
1718         this.fireEvent("click", this, t, e);
1719         
1720         this.hide();
1721     },
1722      onMouseOver : function(e){
1723         var t  = this.findTargetItem(e);
1724         //Roo.log(t);
1725         //if(t){
1726         //    if(t.canActivate && !t.disabled){
1727         //        this.setActiveItem(t, true);
1728         //    }
1729         //}
1730         
1731         this.fireEvent("mouseover", this, e, t);
1732     },
1733     isVisible : function(){
1734         return !this.hidden;
1735     },
1736      onMouseOut : function(e){
1737         var t  = this.findTargetItem(e);
1738         
1739         //if(t ){
1740         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1741         //        this.activeItem.deactivate();
1742         //        delete this.activeItem;
1743         //    }
1744         //}
1745         this.fireEvent("mouseout", this, e, t);
1746     },
1747     
1748     
1749     /**
1750      * Displays this menu relative to another element
1751      * @param {String/HTMLElement/Roo.Element} element The element to align to
1752      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1753      * the element (defaults to this.defaultAlign)
1754      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1755      */
1756     show : function(el, pos, parentMenu){
1757         this.parentMenu = parentMenu;
1758         if(!this.el){
1759             this.render();
1760         }
1761         this.fireEvent("beforeshow", this);
1762         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1763     },
1764      /**
1765      * Displays this menu at a specific xy position
1766      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1767      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1768      */
1769     showAt : function(xy, parentMenu, /* private: */_e){
1770         this.parentMenu = parentMenu;
1771         if(!this.el){
1772             this.render();
1773         }
1774         if(_e !== false){
1775             this.fireEvent("beforeshow", this);
1776             
1777             //xy = this.el.adjustForConstraints(xy);
1778         }
1779         //this.el.setXY(xy);
1780         //this.el.show();
1781         this.hideMenuItems();
1782         this.hidden = false;
1783         this.triggerEl.addClass('open');
1784         this.focus();
1785         this.fireEvent("show", this);
1786     },
1787     
1788     focus : function(){
1789         return;
1790         if(!this.hidden){
1791             this.doFocus.defer(50, this);
1792         }
1793     },
1794
1795     doFocus : function(){
1796         if(!this.hidden){
1797             this.focusEl.focus();
1798         }
1799     },
1800
1801     /**
1802      * Hides this menu and optionally all parent menus
1803      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1804      */
1805     hide : function(deep){
1806         
1807         this.hideMenuItems();
1808         if(this.el && this.isVisible()){
1809             this.fireEvent("beforehide", this);
1810             if(this.activeItem){
1811                 this.activeItem.deactivate();
1812                 this.activeItem = null;
1813             }
1814             this.triggerEl.removeClass('open');;
1815             this.hidden = true;
1816             this.fireEvent("hide", this);
1817         }
1818         if(deep === true && this.parentMenu){
1819             this.parentMenu.hide(true);
1820         }
1821     },
1822     
1823     onTriggerPress  : function(e)
1824     {
1825         
1826         Roo.log('trigger press');
1827         //Roo.log(e.getTarget());
1828        // Roo.log(this.triggerEl.dom);
1829         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1830             return;
1831         }
1832         if (this.isVisible()) {
1833             Roo.log('hide');
1834             this.hide();
1835         } else {
1836             this.show(this.triggerEl, false, false);
1837         }
1838         
1839         
1840     },
1841     
1842          
1843        
1844     
1845     hideMenuItems : function()
1846     {
1847         //$(backdrop).remove()
1848         Roo.select('.open',true).each(function(aa) {
1849             
1850             aa.removeClass('open');
1851           //var parent = getParent($(this))
1852           //var relatedTarget = { relatedTarget: this }
1853           
1854            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1855           //if (e.isDefaultPrevented()) return
1856            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1857         })
1858     },
1859     addxtypeChild : function (tree, cntr) {
1860         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1861           
1862         this.menuitems.add(comp);
1863         return comp;
1864
1865     },
1866     getEl : function()
1867     {
1868         Roo.log(this.el);
1869         return this.el;
1870     }
1871 });
1872
1873  
1874  /*
1875  * - LGPL
1876  *
1877  * menu item
1878  * 
1879  */
1880
1881
1882 /**
1883  * @class Roo.bootstrap.MenuItem
1884  * @extends Roo.bootstrap.Component
1885  * Bootstrap MenuItem class
1886  * @cfg {String} html the menu label
1887  * @cfg {String} href the link
1888  * @cfg {Boolean} preventDefault (true | false) default true
1889  * @cfg {Boolean} isContainer (true | false) default false
1890  * 
1891  * 
1892  * @constructor
1893  * Create a new MenuItem
1894  * @param {Object} config The config object
1895  */
1896
1897
1898 Roo.bootstrap.MenuItem = function(config){
1899     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1900     this.addEvents({
1901         // raw events
1902         /**
1903          * @event click
1904          * The raw click event for the entire grid.
1905          * @param {Roo.EventObject} e
1906          */
1907         "click" : true
1908     });
1909 };
1910
1911 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1912     
1913     href : false,
1914     html : false,
1915     preventDefault: true,
1916     isContainer : false,
1917     
1918     getAutoCreate : function(){
1919         
1920         if(this.isContainer){
1921             return {
1922                 tag: 'li',
1923                 cls: 'dropdown-menu-item'
1924             };
1925         }
1926         
1927         var cfg= {
1928             tag: 'li',
1929             cls: 'dropdown-menu-item',
1930             cn: [
1931                     {
1932                         tag : 'a',
1933                         href : '#',
1934                         html : 'Link'
1935                     }
1936                 ]
1937         };
1938         if (this.parent().type == 'treeview') {
1939             cfg.cls = 'treeview-menu';
1940         }
1941         
1942         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1943         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1944         return cfg;
1945     },
1946     
1947     initEvents: function() {
1948         
1949         //this.el.select('a').on('click', this.onClick, this);
1950         
1951     },
1952     onClick : function(e)
1953     {
1954         Roo.log('item on click ');
1955         //if(this.preventDefault){
1956         //    e.preventDefault();
1957         //}
1958         //this.parent().hideMenuItems();
1959         
1960         this.fireEvent('click', this, e);
1961     },
1962     getEl : function()
1963     {
1964         return this.el;
1965     }
1966 });
1967
1968  
1969
1970  /*
1971  * - LGPL
1972  *
1973  * menu separator
1974  * 
1975  */
1976
1977
1978 /**
1979  * @class Roo.bootstrap.MenuSeparator
1980  * @extends Roo.bootstrap.Component
1981  * Bootstrap MenuSeparator class
1982  * 
1983  * @constructor
1984  * Create a new MenuItem
1985  * @param {Object} config The config object
1986  */
1987
1988
1989 Roo.bootstrap.MenuSeparator = function(config){
1990     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1991 };
1992
1993 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1994     
1995     getAutoCreate : function(){
1996         var cfg = {
1997             cls: 'divider',
1998             tag : 'li'
1999         };
2000         
2001         return cfg;
2002     }
2003    
2004 });
2005
2006  
2007
2008  
2009 /*
2010 <div class="modal fade">
2011   <div class="modal-dialog">
2012     <div class="modal-content">
2013       <div class="modal-header">
2014         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2015         <h4 class="modal-title">Modal title</h4>
2016       </div>
2017       <div class="modal-body">
2018         <p>One fine body&hellip;</p>
2019       </div>
2020       <div class="modal-footer">
2021         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2022         <button type="button" class="btn btn-primary">Save changes</button>
2023       </div>
2024     </div><!-- /.modal-content -->
2025   </div><!-- /.modal-dialog -->
2026 </div><!-- /.modal -->
2027 */
2028 /*
2029  * - LGPL
2030  *
2031  * page contgainer.
2032  * 
2033  */
2034
2035 /**
2036  * @class Roo.bootstrap.Modal
2037  * @extends Roo.bootstrap.Component
2038  * Bootstrap Modal class
2039  * @cfg {String} title Title of dialog
2040  * @cfg {Boolean} specificTitle (true|false) default false
2041  * @cfg {Array} buttons Array of buttons or standard button set..
2042  * @cfg {String} buttonPosition (left|right|center) default right
2043  * @cfg {Boolean} animate (true | false) default true
2044  * 
2045  * @constructor
2046  * Create a new Modal Dialog
2047  * @param {Object} config The config object
2048  */
2049
2050 Roo.bootstrap.Modal = function(config){
2051     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2052     this.addEvents({
2053         // raw events
2054         /**
2055          * @event btnclick
2056          * The raw btnclick event for the button
2057          * @param {Roo.EventObject} e
2058          */
2059         "btnclick" : true
2060     });
2061     this.buttons = this.buttons || [];
2062 };
2063
2064 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2065     
2066     title : 'test dialog',
2067    
2068     buttons : false,
2069     
2070     // set on load...
2071     body:  false,
2072     
2073     specificTitle: false,
2074     
2075     buttonPosition: 'right',
2076     
2077     animate : true,
2078     
2079     onRender : function(ct, position)
2080     {
2081         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2082      
2083         if(!this.el){
2084             var cfg = Roo.apply({},  this.getAutoCreate());
2085             cfg.id = Roo.id();
2086             //if(!cfg.name){
2087             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2088             //}
2089             //if (!cfg.name.length) {
2090             //    delete cfg.name;
2091            // }
2092             if (this.cls) {
2093                 cfg.cls += ' ' + this.cls;
2094             }
2095             if (this.style) {
2096                 cfg.style = this.style;
2097             }
2098             this.el = Roo.get(document.body).createChild(cfg, position);
2099         }
2100         //var type = this.el.dom.type;
2101         
2102         if(this.tabIndex !== undefined){
2103             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2104         }
2105         
2106         
2107         
2108         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2109         this.maskEl.enableDisplayMode("block");
2110         this.maskEl.hide();
2111         //this.el.addClass("x-dlg-modal");
2112     
2113         if (this.buttons.length) {
2114             Roo.each(this.buttons, function(bb) {
2115                 b = Roo.apply({}, bb);
2116                 b.xns = b.xns || Roo.bootstrap;
2117                 b.xtype = b.xtype || 'Button';
2118                 if (typeof(b.listeners) == 'undefined') {
2119                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2120                 }
2121                 
2122                 var btn = Roo.factory(b);
2123                 
2124                 btn.onRender(this.el.select('.modal-footer div').first());
2125                 
2126             },this);
2127         }
2128         // render the children.
2129         var nitems = [];
2130         
2131         if(typeof(this.items) != 'undefined'){
2132             var items = this.items;
2133             delete this.items;
2134
2135             for(var i =0;i < items.length;i++) {
2136                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2137             }
2138         }
2139         
2140         this.items = nitems;
2141         
2142         this.body = this.el.select('.modal-body',true).first();
2143         this.close = this.el.select('.modal-header .close', true).first();
2144         this.footer = this.el.select('.modal-footer',true).first();
2145         this.initEvents();
2146         //this.el.addClass([this.fieldClass, this.cls]);
2147         
2148     },
2149     getAutoCreate : function(){
2150         
2151         
2152         var bdy = {
2153                 cls : 'modal-body',
2154                 html : this.html || ''
2155         };
2156         
2157         var title = {
2158             tag: 'h4',
2159             cls : 'modal-title',
2160             html : this.title
2161         };
2162         
2163         if(this.specificTitle){
2164             title = this.title;
2165         };
2166         
2167         var modal = {
2168             cls: "modal",
2169             style : 'display: none',
2170             cn : [
2171                 {
2172                     cls: "modal-dialog",
2173                     cn : [
2174                         {
2175                             cls : "modal-content",
2176                             cn : [
2177                                 {
2178                                     cls : 'modal-header',
2179                                     cn : [
2180                                         {
2181                                             tag: 'button',
2182                                             cls : 'close',
2183                                             html : '&times'
2184                                         },
2185                                         title
2186                                     ]
2187                                 },
2188                                 bdy,
2189                                 {
2190                                     cls : 'modal-footer',
2191                                     cn : [
2192                                         {
2193                                             tag: 'div',
2194                                             cls: 'btn-' + this.buttonPosition
2195                                         }
2196                                     ]
2197                                     
2198                                 }
2199                                 
2200                                 
2201                             ]
2202                             
2203                         }
2204                     ]
2205                         
2206                 }
2207             ]
2208         };
2209         
2210         if(this.animate){
2211             modal.cls += ' fade';
2212         }
2213         
2214         return modal;
2215           
2216     },
2217     getChildContainer : function() {
2218          
2219          return this.el.select('.modal-body',true).first();
2220         
2221     },
2222     getButtonContainer : function() {
2223          return this.el.select('.modal-footer div',true).first();
2224         
2225     },
2226     initEvents : function()
2227     {
2228         this.el.select('.modal-header .close').on('click', this.hide, this);
2229 //        
2230 //        this.addxtype(this);
2231     },
2232     show : function() {
2233         
2234         if (!this.rendered) {
2235             this.render();
2236         }
2237         
2238         this.el.setStyle('display', 'block');
2239         
2240         if(this.animate){
2241             var _this = this;
2242             (function(){ _this.el.addClass('in'); }).defer(50);
2243         }else{
2244             this.el.addClass('in');
2245         }
2246         
2247         Roo.get(document.body).addClass("x-body-masked");
2248         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2249         this.maskEl.show();
2250         this.el.setStyle('zIndex', '10001');
2251         this.fireEvent('show', this);
2252         
2253         
2254     },
2255     hide : function()
2256     {
2257         this.maskEl.hide();
2258         Roo.get(document.body).removeClass("x-body-masked");
2259         this.el.removeClass('in');
2260         
2261         if(this.animate){
2262             var _this = this;
2263             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2264         }else{
2265             this.el.setStyle('display', 'none');
2266         }
2267         
2268         this.fireEvent('hide', this);
2269     },
2270     
2271     addButton : function(str, cb)
2272     {
2273          
2274         
2275         var b = Roo.apply({}, { html : str } );
2276         b.xns = b.xns || Roo.bootstrap;
2277         b.xtype = b.xtype || 'Button';
2278         if (typeof(b.listeners) == 'undefined') {
2279             b.listeners = { click : cb.createDelegate(this)  };
2280         }
2281         
2282         var btn = Roo.factory(b);
2283            
2284         btn.onRender(this.el.select('.modal-footer div').first());
2285         
2286         return btn;   
2287        
2288     },
2289     
2290     setDefaultButton : function(btn)
2291     {
2292         //this.el.select('.modal-footer').()
2293     },
2294     resizeTo: function(w,h)
2295     {
2296         // skip..
2297     },
2298     setContentSize  : function(w, h)
2299     {
2300         
2301     },
2302     onButtonClick: function(btn,e)
2303     {
2304         //Roo.log([a,b,c]);
2305         this.fireEvent('btnclick', btn.name, e);
2306     },
2307     setTitle: function(str) {
2308         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2309         
2310     }
2311 });
2312
2313
2314 Roo.apply(Roo.bootstrap.Modal,  {
2315     /**
2316          * Button config that displays a single OK button
2317          * @type Object
2318          */
2319         OK :  [{
2320             name : 'ok',
2321             weight : 'primary',
2322             html : 'OK'
2323         }], 
2324         /**
2325          * Button config that displays Yes and No buttons
2326          * @type Object
2327          */
2328         YESNO : [
2329             {
2330                 name  : 'no',
2331                 html : 'No'
2332             },
2333             {
2334                 name  :'yes',
2335                 weight : 'primary',
2336                 html : 'Yes'
2337             }
2338         ],
2339         
2340         /**
2341          * Button config that displays OK and Cancel buttons
2342          * @type Object
2343          */
2344         OKCANCEL : [
2345             {
2346                name : 'cancel',
2347                 html : 'Cancel'
2348             },
2349             {
2350                 name : 'ok',
2351                 weight : 'primary',
2352                 html : 'OK'
2353             }
2354         ],
2355         /**
2356          * Button config that displays Yes, No and Cancel buttons
2357          * @type Object
2358          */
2359         YESNOCANCEL : [
2360             {
2361                 name : 'yes',
2362                 weight : 'primary',
2363                 html : 'Yes'
2364             },
2365             {
2366                 name : 'no',
2367                 html : 'No'
2368             },
2369             {
2370                 name : 'cancel',
2371                 html : 'Cancel'
2372             }
2373         ]
2374 });
2375  /*
2376  * - LGPL
2377  *
2378  * messagebox - can be used as a replace
2379  * 
2380  */
2381 /**
2382  * @class Roo.MessageBox
2383  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2384  * Example usage:
2385  *<pre><code>
2386 // Basic alert:
2387 Roo.Msg.alert('Status', 'Changes saved successfully.');
2388
2389 // Prompt for user data:
2390 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2391     if (btn == 'ok'){
2392         // process text value...
2393     }
2394 });
2395
2396 // Show a dialog using config options:
2397 Roo.Msg.show({
2398    title:'Save Changes?',
2399    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2400    buttons: Roo.Msg.YESNOCANCEL,
2401    fn: processResult,
2402    animEl: 'elId'
2403 });
2404 </code></pre>
2405  * @singleton
2406  */
2407 Roo.bootstrap.MessageBox = function(){
2408     var dlg, opt, mask, waitTimer;
2409     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2410     var buttons, activeTextEl, bwidth;
2411
2412     
2413     // private
2414     var handleButton = function(button){
2415         dlg.hide();
2416         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2417     };
2418
2419     // private
2420     var handleHide = function(){
2421         if(opt && opt.cls){
2422             dlg.el.removeClass(opt.cls);
2423         }
2424         //if(waitTimer){
2425         //    Roo.TaskMgr.stop(waitTimer);
2426         //    waitTimer = null;
2427         //}
2428     };
2429
2430     // private
2431     var updateButtons = function(b){
2432         var width = 0;
2433         if(!b){
2434             buttons["ok"].hide();
2435             buttons["cancel"].hide();
2436             buttons["yes"].hide();
2437             buttons["no"].hide();
2438             //dlg.footer.dom.style.display = 'none';
2439             return width;
2440         }
2441         dlg.footer.dom.style.display = '';
2442         for(var k in buttons){
2443             if(typeof buttons[k] != "function"){
2444                 if(b[k]){
2445                     buttons[k].show();
2446                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2447                     width += buttons[k].el.getWidth()+15;
2448                 }else{
2449                     buttons[k].hide();
2450                 }
2451             }
2452         }
2453         return width;
2454     };
2455
2456     // private
2457     var handleEsc = function(d, k, e){
2458         if(opt && opt.closable !== false){
2459             dlg.hide();
2460         }
2461         if(e){
2462             e.stopEvent();
2463         }
2464     };
2465
2466     return {
2467         /**
2468          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2469          * @return {Roo.BasicDialog} The BasicDialog element
2470          */
2471         getDialog : function(){
2472            if(!dlg){
2473                 dlg = new Roo.bootstrap.Modal( {
2474                     //draggable: true,
2475                     //resizable:false,
2476                     //constraintoviewport:false,
2477                     //fixedcenter:true,
2478                     //collapsible : false,
2479                     //shim:true,
2480                     //modal: true,
2481                   //  width:400,
2482                   //  height:100,
2483                     //buttonAlign:"center",
2484                     closeClick : function(){
2485                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2486                             handleButton("no");
2487                         }else{
2488                             handleButton("cancel");
2489                         }
2490                     }
2491                 });
2492                 dlg.render();
2493                 dlg.on("hide", handleHide);
2494                 mask = dlg.mask;
2495                 //dlg.addKeyListener(27, handleEsc);
2496                 buttons = {};
2497                 this.buttons = buttons;
2498                 var bt = this.buttonText;
2499                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2500                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2501                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2502                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2503                 Roo.log(buttons)
2504                 bodyEl = dlg.body.createChild({
2505
2506                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2507                         '<textarea class="roo-mb-textarea"></textarea>' +
2508                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2509                 });
2510                 msgEl = bodyEl.dom.firstChild;
2511                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2512                 textboxEl.enableDisplayMode();
2513                 textboxEl.addKeyListener([10,13], function(){
2514                     if(dlg.isVisible() && opt && opt.buttons){
2515                         if(opt.buttons.ok){
2516                             handleButton("ok");
2517                         }else if(opt.buttons.yes){
2518                             handleButton("yes");
2519                         }
2520                     }
2521                 });
2522                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2523                 textareaEl.enableDisplayMode();
2524                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2525                 progressEl.enableDisplayMode();
2526                 var pf = progressEl.dom.firstChild;
2527                 if (pf) {
2528                     pp = Roo.get(pf.firstChild);
2529                     pp.setHeight(pf.offsetHeight);
2530                 }
2531                 
2532             }
2533             return dlg;
2534         },
2535
2536         /**
2537          * Updates the message box body text
2538          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2539          * the XHTML-compliant non-breaking space character '&amp;#160;')
2540          * @return {Roo.MessageBox} This message box
2541          */
2542         updateText : function(text){
2543             if(!dlg.isVisible() && !opt.width){
2544                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2545             }
2546             msgEl.innerHTML = text || '&#160;';
2547       
2548             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2549             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2550             var w = Math.max(
2551                     Math.min(opt.width || cw , this.maxWidth), 
2552                     Math.max(opt.minWidth || this.minWidth, bwidth)
2553             );
2554             if(opt.prompt){
2555                 activeTextEl.setWidth(w);
2556             }
2557             if(dlg.isVisible()){
2558                 dlg.fixedcenter = false;
2559             }
2560             // to big, make it scroll. = But as usual stupid IE does not support
2561             // !important..
2562             
2563             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2564                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2565                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2566             } else {
2567                 bodyEl.dom.style.height = '';
2568                 bodyEl.dom.style.overflowY = '';
2569             }
2570             if (cw > w) {
2571                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2572             } else {
2573                 bodyEl.dom.style.overflowX = '';
2574             }
2575             
2576             dlg.setContentSize(w, bodyEl.getHeight());
2577             if(dlg.isVisible()){
2578                 dlg.fixedcenter = true;
2579             }
2580             return this;
2581         },
2582
2583         /**
2584          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2585          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2586          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2587          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2588          * @return {Roo.MessageBox} This message box
2589          */
2590         updateProgress : function(value, text){
2591             if(text){
2592                 this.updateText(text);
2593             }
2594             if (pp) { // weird bug on my firefox - for some reason this is not defined
2595                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2596             }
2597             return this;
2598         },        
2599
2600         /**
2601          * Returns true if the message box is currently displayed
2602          * @return {Boolean} True if the message box is visible, else false
2603          */
2604         isVisible : function(){
2605             return dlg && dlg.isVisible();  
2606         },
2607
2608         /**
2609          * Hides the message box if it is displayed
2610          */
2611         hide : function(){
2612             if(this.isVisible()){
2613                 dlg.hide();
2614             }  
2615         },
2616
2617         /**
2618          * Displays a new message box, or reinitializes an existing message box, based on the config options
2619          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2620          * The following config object properties are supported:
2621          * <pre>
2622 Property    Type             Description
2623 ----------  ---------------  ------------------------------------------------------------------------------------
2624 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2625                                    closes (defaults to undefined)
2626 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2627                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2628 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2629                                    progress and wait dialogs will ignore this property and always hide the
2630                                    close button as they can only be closed programmatically.
2631 cls               String           A custom CSS class to apply to the message box element
2632 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2633                                    displayed (defaults to 75)
2634 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2635                                    function will be btn (the name of the button that was clicked, if applicable,
2636                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2637                                    Progress and wait dialogs will ignore this option since they do not respond to
2638                                    user actions and can only be closed programmatically, so any required function
2639                                    should be called by the same code after it closes the dialog.
2640 icon              String           A CSS class that provides a background image to be used as an icon for
2641                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2642 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2643 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2644 modal             Boolean          False to allow user interaction with the page while the message box is
2645                                    displayed (defaults to true)
2646 msg               String           A string that will replace the existing message box body text (defaults
2647                                    to the XHTML-compliant non-breaking space character '&#160;')
2648 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2649 progress          Boolean          True to display a progress bar (defaults to false)
2650 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2651 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2652 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2653 title             String           The title text
2654 value             String           The string value to set into the active textbox element if displayed
2655 wait              Boolean          True to display a progress bar (defaults to false)
2656 width             Number           The width of the dialog in pixels
2657 </pre>
2658          *
2659          * Example usage:
2660          * <pre><code>
2661 Roo.Msg.show({
2662    title: 'Address',
2663    msg: 'Please enter your address:',
2664    width: 300,
2665    buttons: Roo.MessageBox.OKCANCEL,
2666    multiline: true,
2667    fn: saveAddress,
2668    animEl: 'addAddressBtn'
2669 });
2670 </code></pre>
2671          * @param {Object} config Configuration options
2672          * @return {Roo.MessageBox} This message box
2673          */
2674         show : function(options)
2675         {
2676             
2677             // this causes nightmares if you show one dialog after another
2678             // especially on callbacks..
2679              
2680             if(this.isVisible()){
2681                 
2682                 this.hide();
2683                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2684                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2685                 Roo.log("New Dialog Message:" +  options.msg )
2686                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2687                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2688                 
2689             }
2690             var d = this.getDialog();
2691             opt = options;
2692             d.setTitle(opt.title || "&#160;");
2693             d.close.setDisplayed(opt.closable !== false);
2694             activeTextEl = textboxEl;
2695             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2696             if(opt.prompt){
2697                 if(opt.multiline){
2698                     textboxEl.hide();
2699                     textareaEl.show();
2700                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2701                         opt.multiline : this.defaultTextHeight);
2702                     activeTextEl = textareaEl;
2703                 }else{
2704                     textboxEl.show();
2705                     textareaEl.hide();
2706                 }
2707             }else{
2708                 textboxEl.hide();
2709                 textareaEl.hide();
2710             }
2711             progressEl.setDisplayed(opt.progress === true);
2712             this.updateProgress(0);
2713             activeTextEl.dom.value = opt.value || "";
2714             if(opt.prompt){
2715                 dlg.setDefaultButton(activeTextEl);
2716             }else{
2717                 var bs = opt.buttons;
2718                 var db = null;
2719                 if(bs && bs.ok){
2720                     db = buttons["ok"];
2721                 }else if(bs && bs.yes){
2722                     db = buttons["yes"];
2723                 }
2724                 dlg.setDefaultButton(db);
2725             }
2726             bwidth = updateButtons(opt.buttons);
2727             this.updateText(opt.msg);
2728             if(opt.cls){
2729                 d.el.addClass(opt.cls);
2730             }
2731             d.proxyDrag = opt.proxyDrag === true;
2732             d.modal = opt.modal !== false;
2733             d.mask = opt.modal !== false ? mask : false;
2734             if(!d.isVisible()){
2735                 // force it to the end of the z-index stack so it gets a cursor in FF
2736                 document.body.appendChild(dlg.el.dom);
2737                 d.animateTarget = null;
2738                 d.show(options.animEl);
2739             }
2740             return this;
2741         },
2742
2743         /**
2744          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2745          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2746          * and closing the message box when the process is complete.
2747          * @param {String} title The title bar text
2748          * @param {String} msg The message box body text
2749          * @return {Roo.MessageBox} This message box
2750          */
2751         progress : function(title, msg){
2752             this.show({
2753                 title : title,
2754                 msg : msg,
2755                 buttons: false,
2756                 progress:true,
2757                 closable:false,
2758                 minWidth: this.minProgressWidth,
2759                 modal : true
2760             });
2761             return this;
2762         },
2763
2764         /**
2765          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2766          * If a callback function is passed it will be called after the user clicks the button, and the
2767          * id of the button that was clicked will be passed as the only parameter to the callback
2768          * (could also be the top-right close button).
2769          * @param {String} title The title bar text
2770          * @param {String} msg The message box body text
2771          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2772          * @param {Object} scope (optional) The scope of the callback function
2773          * @return {Roo.MessageBox} This message box
2774          */
2775         alert : function(title, msg, fn, scope){
2776             this.show({
2777                 title : title,
2778                 msg : msg,
2779                 buttons: this.OK,
2780                 fn: fn,
2781                 scope : scope,
2782                 modal : true
2783             });
2784             return this;
2785         },
2786
2787         /**
2788          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2789          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2790          * You are responsible for closing the message box when the process is complete.
2791          * @param {String} msg The message box body text
2792          * @param {String} title (optional) The title bar text
2793          * @return {Roo.MessageBox} This message box
2794          */
2795         wait : function(msg, title){
2796             this.show({
2797                 title : title,
2798                 msg : msg,
2799                 buttons: false,
2800                 closable:false,
2801                 progress:true,
2802                 modal:true,
2803                 width:300,
2804                 wait:true
2805             });
2806             waitTimer = Roo.TaskMgr.start({
2807                 run: function(i){
2808                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2809                 },
2810                 interval: 1000
2811             });
2812             return this;
2813         },
2814
2815         /**
2816          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2817          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2818          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2819          * @param {String} title The title bar text
2820          * @param {String} msg The message box body text
2821          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2822          * @param {Object} scope (optional) The scope of the callback function
2823          * @return {Roo.MessageBox} This message box
2824          */
2825         confirm : function(title, msg, fn, scope){
2826             this.show({
2827                 title : title,
2828                 msg : msg,
2829                 buttons: this.YESNO,
2830                 fn: fn,
2831                 scope : scope,
2832                 modal : true
2833             });
2834             return this;
2835         },
2836
2837         /**
2838          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2839          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2840          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2841          * (could also be the top-right close button) and the text that was entered will be passed as the two
2842          * parameters to the callback.
2843          * @param {String} title The title bar text
2844          * @param {String} msg The message box body text
2845          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2846          * @param {Object} scope (optional) The scope of the callback function
2847          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2848          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2849          * @return {Roo.MessageBox} This message box
2850          */
2851         prompt : function(title, msg, fn, scope, multiline){
2852             this.show({
2853                 title : title,
2854                 msg : msg,
2855                 buttons: this.OKCANCEL,
2856                 fn: fn,
2857                 minWidth:250,
2858                 scope : scope,
2859                 prompt:true,
2860                 multiline: multiline,
2861                 modal : true
2862             });
2863             return this;
2864         },
2865
2866         /**
2867          * Button config that displays a single OK button
2868          * @type Object
2869          */
2870         OK : {ok:true},
2871         /**
2872          * Button config that displays Yes and No buttons
2873          * @type Object
2874          */
2875         YESNO : {yes:true, no:true},
2876         /**
2877          * Button config that displays OK and Cancel buttons
2878          * @type Object
2879          */
2880         OKCANCEL : {ok:true, cancel:true},
2881         /**
2882          * Button config that displays Yes, No and Cancel buttons
2883          * @type Object
2884          */
2885         YESNOCANCEL : {yes:true, no:true, cancel:true},
2886
2887         /**
2888          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2889          * @type Number
2890          */
2891         defaultTextHeight : 75,
2892         /**
2893          * The maximum width in pixels of the message box (defaults to 600)
2894          * @type Number
2895          */
2896         maxWidth : 600,
2897         /**
2898          * The minimum width in pixels of the message box (defaults to 100)
2899          * @type Number
2900          */
2901         minWidth : 100,
2902         /**
2903          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2904          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2905          * @type Number
2906          */
2907         minProgressWidth : 250,
2908         /**
2909          * An object containing the default button text strings that can be overriden for localized language support.
2910          * Supported properties are: ok, cancel, yes and no.
2911          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2912          * @type Object
2913          */
2914         buttonText : {
2915             ok : "OK",
2916             cancel : "Cancel",
2917             yes : "Yes",
2918             no : "No"
2919         }
2920     };
2921 }();
2922
2923 /**
2924  * Shorthand for {@link Roo.MessageBox}
2925  */
2926 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2927 Roo.Msg = Roo.Msg || Roo.MessageBox;
2928 /*
2929  * - LGPL
2930  *
2931  * navbar
2932  * 
2933  */
2934
2935 /**
2936  * @class Roo.bootstrap.Navbar
2937  * @extends Roo.bootstrap.Component
2938  * Bootstrap Navbar class
2939
2940  * @constructor
2941  * Create a new Navbar
2942  * @param {Object} config The config object
2943  */
2944
2945
2946 Roo.bootstrap.Navbar = function(config){
2947     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2948     
2949 };
2950
2951 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2952     
2953     
2954    
2955     // private
2956     navItems : false,
2957     loadMask : false,
2958     
2959     
2960     getAutoCreate : function(){
2961         
2962         
2963         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2964         
2965     },
2966     
2967     initEvents :function ()
2968     {
2969         //Roo.log(this.el.select('.navbar-toggle',true));
2970         this.el.select('.navbar-toggle',true).on('click', function() {
2971            // Roo.log('click');
2972             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2973         }, this);
2974         
2975         var mark = {
2976             tag: "div",
2977             cls:"x-dlg-mask"
2978         }
2979         
2980         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2981         
2982         var size = this.el.getSize();
2983         this.maskEl.setSize(size.width, size.height);
2984         this.maskEl.enableDisplayMode("block");
2985         this.maskEl.hide();
2986         
2987         if(this.loadMask){
2988             this.maskEl.show();
2989         }
2990     },
2991     
2992     
2993     getChildContainer : function()
2994     {
2995         if (this.el.select('.collapse').getCount()) {
2996             return this.el.select('.collapse',true).first();
2997         }
2998         
2999         return this.el;
3000     },
3001     
3002     mask : function()
3003     {
3004         this.maskEl.show();
3005     },
3006     
3007     unmask : function()
3008     {
3009         this.maskEl.hide();
3010     } 
3011     
3012     
3013     
3014     
3015 });
3016
3017
3018
3019  
3020
3021  /*
3022  * - LGPL
3023  *
3024  * navbar
3025  * 
3026  */
3027
3028 /**
3029  * @class Roo.bootstrap.NavSimplebar
3030  * @extends Roo.bootstrap.Navbar
3031  * Bootstrap Sidebar class
3032  *
3033  * @cfg {Boolean} inverse is inverted color
3034  * 
3035  * @cfg {String} type (nav | pills | tabs)
3036  * @cfg {Boolean} arrangement stacked | justified
3037  * @cfg {String} align (left | right) alignment
3038  * 
3039  * @cfg {Boolean} main (true|false) main nav bar? default false
3040  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3041  * 
3042  * @cfg {String} tag (header|footer|nav|div) default is nav 
3043
3044  * 
3045  * 
3046  * 
3047  * @constructor
3048  * Create a new Sidebar
3049  * @param {Object} config The config object
3050  */
3051
3052
3053 Roo.bootstrap.NavSimplebar = function(config){
3054     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3055 };
3056
3057 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3058     
3059     inverse: false,
3060     
3061     type: false,
3062     arrangement: '',
3063     align : false,
3064     
3065     
3066     
3067     main : false,
3068     
3069     
3070     tag : false,
3071     
3072     
3073     getAutoCreate : function(){
3074         
3075         
3076         var cfg = {
3077             tag : this.tag || 'div',
3078             cls : 'navbar'
3079         };
3080           
3081         
3082         cfg.cn = [
3083             {
3084                 cls: 'nav',
3085                 tag : 'ul'
3086             }
3087         ];
3088         
3089          
3090         this.type = this.type || 'nav';
3091         if (['tabs','pills'].indexOf(this.type)!==-1) {
3092             cfg.cn[0].cls += ' nav-' + this.type
3093         
3094         
3095         } else {
3096             if (this.type!=='nav') {
3097                 Roo.log('nav type must be nav/tabs/pills')
3098             }
3099             cfg.cn[0].cls += ' navbar-nav'
3100         }
3101         
3102         
3103         
3104         
3105         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3106             cfg.cn[0].cls += ' nav-' + this.arrangement;
3107         }
3108         
3109         
3110         if (this.align === 'right') {
3111             cfg.cn[0].cls += ' navbar-right';
3112         }
3113         
3114         if (this.inverse) {
3115             cfg.cls += ' navbar-inverse';
3116             
3117         }
3118         
3119         
3120         return cfg;
3121     
3122         
3123     }
3124     
3125     
3126     
3127 });
3128
3129
3130
3131  
3132
3133  
3134        /*
3135  * - LGPL
3136  *
3137  * navbar
3138  * 
3139  */
3140
3141 /**
3142  * @class Roo.bootstrap.NavHeaderbar
3143  * @extends Roo.bootstrap.NavSimplebar
3144  * Bootstrap Sidebar class
3145  *
3146  * @cfg {String} brand what is brand
3147  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3148  * @cfg {String} brand_href href of the brand
3149  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3150  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3151  * 
3152  * @constructor
3153  * Create a new Sidebar
3154  * @param {Object} config The config object
3155  */
3156
3157
3158 Roo.bootstrap.NavHeaderbar = function(config){
3159     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3160 };
3161
3162 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3163     
3164     position: '',
3165     brand: '',
3166     brand_href: false,
3167     srButton : true,
3168     autohide : false,
3169     
3170     getAutoCreate : function(){
3171         
3172         var   cfg = {
3173             tag: this.nav || 'nav',
3174             cls: 'navbar',
3175             role: 'navigation',
3176             cn: []
3177         };
3178         
3179         if(this.srButton){
3180             cfg.cn.push({
3181                 tag: 'div',
3182                 cls: 'navbar-header',
3183                 cn: [
3184                     {
3185                         tag: 'button',
3186                         type: 'button',
3187                         cls: 'navbar-toggle',
3188                         'data-toggle': 'collapse',
3189                         cn: [
3190                             {
3191                                 tag: 'span',
3192                                 cls: 'sr-only',
3193                                 html: 'Toggle navigation'
3194                             },
3195                             {
3196                                 tag: 'span',
3197                                 cls: 'icon-bar'
3198                             },
3199                             {
3200                                 tag: 'span',
3201                                 cls: 'icon-bar'
3202                             },
3203                             {
3204                                 tag: 'span',
3205                                 cls: 'icon-bar'
3206                             }
3207                         ]
3208                     }
3209                 ]
3210             });
3211         }
3212         
3213         cfg.cn.push({
3214             tag: 'div',
3215             cls: 'collapse navbar-collapse',
3216             cn : []
3217         });
3218         
3219         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3220         
3221         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3222             cfg.cls += ' navbar-' + this.position;
3223             
3224             // tag can override this..
3225             
3226             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3227         }
3228         
3229         if (this.brand !== '') {
3230             cfg.cn[0].cn.push({
3231                 tag: 'a',
3232                 href: this.brand_href ? this.brand_href : '#',
3233                 cls: 'navbar-brand',
3234                 cn: [
3235                 this.brand
3236                 ]
3237             });
3238         }
3239         
3240         if(this.main){
3241             cfg.cls += ' main-nav';
3242         }
3243         
3244         
3245         return cfg;
3246
3247         
3248     },
3249     initEvents : function()
3250     {
3251         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3252         
3253         if (this.autohide) {
3254             
3255             var prevScroll = 0;
3256             var ft = this.el;
3257             
3258             Roo.get(document).on('scroll',function(e) {
3259                 var ns = Roo.get(document).getScroll().top;
3260                 var os = prevScroll;
3261                 prevScroll = ns;
3262                 
3263                 if(ns > os){
3264                     ft.removeClass('slideDown');
3265                     ft.addClass('slideUp');
3266                     return;
3267                 }
3268                 ft.removeClass('slideUp');
3269                 ft.addClass('slideDown');
3270                  
3271               
3272           },this);
3273         }
3274     }    
3275           
3276       
3277     
3278     
3279 });
3280
3281
3282
3283  
3284
3285  /*
3286  * - LGPL
3287  *
3288  * navbar
3289  * 
3290  */
3291
3292 /**
3293  * @class Roo.bootstrap.NavSidebar
3294  * @extends Roo.bootstrap.Navbar
3295  * Bootstrap Sidebar class
3296  * 
3297  * @constructor
3298  * Create a new Sidebar
3299  * @param {Object} config The config object
3300  */
3301
3302
3303 Roo.bootstrap.NavSidebar = function(config){
3304     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3305 };
3306
3307 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3308     
3309     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3310     
3311     getAutoCreate : function(){
3312         
3313         
3314         return  {
3315             tag: 'div',
3316             cls: 'sidebar sidebar-nav'
3317         };
3318     
3319         
3320     }
3321     
3322     
3323     
3324 });
3325
3326
3327
3328  
3329
3330  /*
3331  * - LGPL
3332  *
3333  * nav group
3334  * 
3335  */
3336
3337 /**
3338  * @class Roo.bootstrap.NavGroup
3339  * @extends Roo.bootstrap.Component
3340  * Bootstrap NavGroup class
3341  * @cfg {String} align left | right
3342  * @cfg {Boolean} inverse false | true
3343  * @cfg {String} type (nav|pills|tab) default nav
3344  * @cfg {String} navId - reference Id for navbar.
3345
3346  * 
3347  * @constructor
3348  * Create a new nav group
3349  * @param {Object} config The config object
3350  */
3351
3352 Roo.bootstrap.NavGroup = function(config){
3353     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3354     this.navItems = [];
3355    
3356     Roo.bootstrap.NavGroup.register(this);
3357      this.addEvents({
3358         /**
3359              * @event changed
3360              * Fires when the active item changes
3361              * @param {Roo.bootstrap.NavGroup} this
3362              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3363              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3364          */
3365         'changed': true
3366      });
3367     
3368 };
3369
3370 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3371     
3372     align: '',
3373     inverse: false,
3374     form: false,
3375     type: 'nav',
3376     navId : '',
3377     // private
3378     
3379     navItems : false, 
3380     
3381     getAutoCreate : function()
3382     {
3383         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3384         
3385         cfg = {
3386             tag : 'ul',
3387             cls: 'nav' 
3388         }
3389         
3390         if (['tabs','pills'].indexOf(this.type)!==-1) {
3391             cfg.cls += ' nav-' + this.type
3392         } else {
3393             if (this.type!=='nav') {
3394                 Roo.log('nav type must be nav/tabs/pills')
3395             }
3396             cfg.cls += ' navbar-nav'
3397         }
3398         
3399         if (this.parent().sidebar) {
3400             cfg = {
3401                 tag: 'ul',
3402                 cls: 'dashboard-menu sidebar-menu'
3403             }
3404             
3405             return cfg;
3406         }
3407         
3408         if (this.form === true) {
3409             cfg = {
3410                 tag: 'form',
3411                 cls: 'navbar-form'
3412             }
3413             
3414             if (this.align === 'right') {
3415                 cfg.cls += ' navbar-right';
3416             } else {
3417                 cfg.cls += ' navbar-left';
3418             }
3419         }
3420         
3421         if (this.align === 'right') {
3422             cfg.cls += ' navbar-right';
3423         }
3424         
3425         if (this.inverse) {
3426             cfg.cls += ' navbar-inverse';
3427             
3428         }
3429         
3430         
3431         return cfg;
3432     },
3433     /**
3434     * sets the active Navigation item
3435     * @param {Roo.bootstrap.NavItem} the new current navitem
3436     */
3437     setActiveItem : function(item)
3438     {
3439         var prev = false;
3440         Roo.each(this.navItems, function(v){
3441             if (v == item) {
3442                 return ;
3443             }
3444             if (v.isActive()) {
3445                 v.setActive(false, true);
3446                 prev = v;
3447                 
3448             }
3449             
3450         });
3451
3452         item.setActive(true, true);
3453         this.fireEvent('changed', this, item, prev);
3454         
3455         
3456     },
3457     /**
3458     * gets the active Navigation item
3459     * @return {Roo.bootstrap.NavItem} the current navitem
3460     */
3461     getActive : function()
3462     {
3463         
3464         var prev = false;
3465         Roo.each(this.navItems, function(v){
3466             
3467             if (v.isActive()) {
3468                 prev = v;
3469                 
3470             }
3471             
3472         });
3473         return prev;
3474     },
3475     
3476     indexOfNav : function()
3477     {
3478         
3479         var prev = false;
3480         Roo.each(this.navItems, function(v,i){
3481             
3482             if (v.isActive()) {
3483                 prev = i;
3484                 
3485             }
3486             
3487         });
3488         return prev;
3489     },
3490     /**
3491     * adds a Navigation item
3492     * @param {Roo.bootstrap.NavItem} the navitem to add
3493     */
3494     addItem : function(cfg)
3495     {
3496         var cn = new Roo.bootstrap.NavItem(cfg);
3497         this.register(cn);
3498         cn.parentId = this.id;
3499         cn.onRender(this.el, null);
3500         return cn;
3501     },
3502     /**
3503     * register a Navigation item
3504     * @param {Roo.bootstrap.NavItem} the navitem to add
3505     */
3506     register : function(item)
3507     {
3508         this.navItems.push( item);
3509         item.navId = this.navId;
3510     
3511     },
3512     
3513     /**
3514     * clear all the Navigation item
3515     */
3516    
3517     clearAll : function()
3518     {
3519         this.navItems = [];
3520         this.el.dom.innerHTML = '';
3521     },
3522     
3523     getNavItem: function(tabId)
3524     {
3525         var ret = false;
3526         Roo.each(this.navItems, function(e) {
3527             if (e.tabId == tabId) {
3528                ret =  e;
3529                return false;
3530             }
3531             return true;
3532             
3533         });
3534         return ret;
3535     },
3536     
3537     setActiveNext : function()
3538     {
3539         var i = this.indexOfNav(this.getActive());
3540         if (i > this.navItems.length) {
3541             return;
3542         }
3543         this.setActiveItem(this.navItems[i+1]);
3544     },
3545     setActivePrev : function()
3546     {
3547         var i = this.indexOfNav(this.getActive());
3548         if (i  < 1) {
3549             return;
3550         }
3551         this.setActiveItem(this.navItems[i-1]);
3552     },
3553     clearWasActive : function(except) {
3554         Roo.each(this.navItems, function(e) {
3555             if (e.tabId != except.tabId && e.was_active) {
3556                e.was_active = false;
3557                return false;
3558             }
3559             return true;
3560             
3561         });
3562     },
3563     getWasActive : function ()
3564     {
3565         var r = false;
3566         Roo.each(this.navItems, function(e) {
3567             if (e.was_active) {
3568                r = e;
3569                return false;
3570             }
3571             return true;
3572             
3573         });
3574         return r;
3575     }
3576     
3577     
3578 });
3579
3580  
3581 Roo.apply(Roo.bootstrap.NavGroup, {
3582     
3583     groups: {},
3584      /**
3585     * register a Navigation Group
3586     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3587     */
3588     register : function(navgrp)
3589     {
3590         this.groups[navgrp.navId] = navgrp;
3591         
3592     },
3593     /**
3594     * fetch a Navigation Group based on the navigation ID
3595     * @param {string} the navgroup to add
3596     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3597     */
3598     get: function(navId) {
3599         if (typeof(this.groups[navId]) == 'undefined') {
3600             return false;
3601             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3602         }
3603         return this.groups[navId] ;
3604     }
3605     
3606     
3607     
3608 });
3609
3610  /*
3611  * - LGPL
3612  *
3613  * row
3614  * 
3615  */
3616
3617 /**
3618  * @class Roo.bootstrap.NavItem
3619  * @extends Roo.bootstrap.Component
3620  * Bootstrap Navbar.NavItem class
3621  * @cfg {String} href  link to
3622  * @cfg {String} html content of button
3623  * @cfg {String} badge text inside badge
3624  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3625  * @cfg {String} glyphicon name of glyphicon
3626  * @cfg {String} icon name of font awesome icon
3627  * @cfg {Boolean} active Is item active
3628  * @cfg {Boolean} disabled Is item disabled
3629  
3630  * @cfg {Boolean} preventDefault (true | false) default false
3631  * @cfg {String} tabId the tab that this item activates.
3632  * @cfg {String} tagtype (a|span) render as a href or span?
3633   
3634  * @constructor
3635  * Create a new Navbar Item
3636  * @param {Object} config The config object
3637  */
3638 Roo.bootstrap.NavItem = function(config){
3639     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3640     this.addEvents({
3641         // raw events
3642         /**
3643          * @event click
3644          * The raw click event for the entire grid.
3645          * @param {Roo.EventObject} e
3646          */
3647         "click" : true,
3648          /**
3649             * @event changed
3650             * Fires when the active item active state changes
3651             * @param {Roo.bootstrap.NavItem} this
3652             * @param {boolean} state the new state
3653              
3654          */
3655         'changed': true
3656     });
3657    
3658 };
3659
3660 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3661     
3662     href: false,
3663     html: '',
3664     badge: '',
3665     icon: false,
3666     glyphicon: false,
3667     active: false,
3668     preventDefault : false,
3669     tabId : false,
3670     tagtype : 'a',
3671     disabled : false,
3672     
3673     was_active : false,
3674     
3675     getAutoCreate : function(){
3676          
3677         var cfg = {
3678             tag: 'li',
3679             cls: 'nav-item'
3680             
3681         }
3682         if (this.active) {
3683             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3684         }
3685         if (this.disabled) {
3686             cfg.cls += ' disabled';
3687         }
3688         
3689         if (this.href || this.html || this.glyphicon || this.icon) {
3690             cfg.cn = [
3691                 {
3692                     tag: this.tagtype,
3693                     href : this.href || "#",
3694                     html: this.html || ''
3695                 }
3696             ];
3697             
3698             if (this.icon) {
3699                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3700             }
3701
3702             if(this.glyphicon) {
3703                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3704             }
3705             
3706             if (this.menu) {
3707                 
3708                 cfg.cn[0].html += " <span class='caret'></span>";
3709              
3710             }
3711             
3712             if (this.badge !== '') {
3713                  
3714                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3715             }
3716         }
3717         
3718         
3719         
3720         return cfg;
3721     },
3722     initEvents: function() {
3723        // Roo.log('init events?');
3724        // Roo.log(this.el.dom);
3725         if (typeof (this.menu) != 'undefined') {
3726             this.menu.parentType = this.xtype;
3727             this.menu.triggerEl = this.el;
3728             this.addxtype(Roo.apply({}, this.menu));
3729         }
3730
3731        
3732         this.el.select('a',true).on('click', this.onClick, this);
3733         // at this point parent should be available..
3734         this.parent().register(this);
3735     },
3736     
3737     onClick : function(e)
3738     {
3739          
3740         if(this.preventDefault){
3741             e.preventDefault();
3742         }
3743         if (this.disabled) {
3744             return;
3745         }
3746         
3747         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3748         if (tg && tg.transition) {
3749             Roo.log("waiting for the transitionend");
3750             return;
3751         }
3752         
3753         Roo.log("fire event clicked");
3754         if(this.fireEvent('click', this, e) === false){
3755             return;
3756         };
3757         var p = this.parent();
3758         if (['tabs','pills'].indexOf(p.type)!==-1) {
3759             if (typeof(p.setActiveItem) !== 'undefined') {
3760                 p.setActiveItem(this);
3761             }
3762         }
3763         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3764         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3765             // remove the collapsed menu expand...
3766             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3767         }
3768         
3769     },
3770     
3771     isActive: function () {
3772         return this.active
3773     },
3774     setActive : function(state, fire, is_was_active)
3775     {
3776         if (this.active && !state & this.navId) {
3777             this.was_active = true;
3778             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3779             if (nv) {
3780                 nv.clearWasActive(this);
3781             }
3782             
3783         }
3784         this.active = state;
3785         
3786         if (!state ) {
3787             this.el.removeClass('active');
3788         } else if (!this.el.hasClass('active')) {
3789             this.el.addClass('active');
3790         }
3791         if (fire) {
3792             this.fireEvent('changed', this, state);
3793         }
3794         
3795         // show a panel if it's registered and related..
3796         
3797         if (!this.navId || !this.tabId || !state || is_was_active) {
3798             return;
3799         }
3800         
3801         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3802         if (!tg) {
3803             return;
3804         }
3805         var pan = tg.getPanelByName(this.tabId);
3806         if (!pan) {
3807             return;
3808         }
3809         // if we can not flip to new panel - go back to old nav highlight..
3810         if (false == tg.showPanel(pan)) {
3811             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3812             if (nv) {
3813                 var onav = nv.getWasActive();
3814                 if (onav) {
3815                     onav.setActive(true, false, true);
3816                 }
3817             }
3818             
3819         }
3820         
3821         
3822         
3823     },
3824      // this should not be here...
3825     setDisabled : function(state)
3826     {
3827         this.disabled = state;
3828         if (!state ) {
3829             this.el.removeClass('disabled');
3830         } else if (!this.el.hasClass('disabled')) {
3831             this.el.addClass('disabled');
3832         }
3833         
3834     }
3835 });
3836  
3837
3838  /*
3839  * - LGPL
3840  *
3841  * sidebar item
3842  *
3843  *  li
3844  *    <span> icon </span>
3845  *    <span> text </span>
3846  *    <span>badge </span>
3847  */
3848
3849 /**
3850  * @class Roo.bootstrap.NavSidebarItem
3851  * @extends Roo.bootstrap.NavItem
3852  * Bootstrap Navbar.NavSidebarItem class
3853  * @constructor
3854  * Create a new Navbar Button
3855  * @param {Object} config The config object
3856  */
3857 Roo.bootstrap.NavSidebarItem = function(config){
3858     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3859     this.addEvents({
3860         // raw events
3861         /**
3862          * @event click
3863          * The raw click event for the entire grid.
3864          * @param {Roo.EventObject} e
3865          */
3866         "click" : true,
3867          /**
3868             * @event changed
3869             * Fires when the active item active state changes
3870             * @param {Roo.bootstrap.NavSidebarItem} this
3871             * @param {boolean} state the new state
3872              
3873          */
3874         'changed': true
3875     });
3876    
3877 };
3878
3879 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3880     
3881     
3882     getAutoCreate : function(){
3883         
3884         
3885         var a = {
3886                 tag: 'a',
3887                 href : this.href || '#',
3888                 cls: '',
3889                 html : '',
3890                 cn : []
3891         };
3892         var cfg = {
3893             tag: 'li',
3894             cls: '',
3895             cn: [ a ]
3896         }
3897         var span = {
3898             tag: 'span',
3899             html : this.html || ''
3900         }
3901         
3902         
3903         if (this.active) {
3904             cfg.cls += ' active';
3905         }
3906         
3907         // left icon..
3908         if (this.glyphicon || this.icon) {
3909             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3910             a.cn.push({ tag : 'i', cls : c }) ;
3911         }
3912         // html..
3913         a.cn.push(span);
3914         // then badge..
3915         if (this.badge !== '') {
3916             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3917         }
3918         // fi
3919         if (this.menu) {
3920             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3921             a.cls += 'dropdown-toggle treeview' ;
3922             
3923         }
3924         
3925         
3926         
3927         return cfg;
3928          
3929            
3930     }
3931    
3932      
3933  
3934 });
3935  
3936
3937  /*
3938  * - LGPL
3939  *
3940  * row
3941  * 
3942  */
3943
3944 /**
3945  * @class Roo.bootstrap.Row
3946  * @extends Roo.bootstrap.Component
3947  * Bootstrap Row class (contains columns...)
3948  * 
3949  * @constructor
3950  * Create a new Row
3951  * @param {Object} config The config object
3952  */
3953
3954 Roo.bootstrap.Row = function(config){
3955     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3956 };
3957
3958 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3959     
3960     getAutoCreate : function(){
3961        return {
3962             cls: 'row clearfix'
3963        };
3964     }
3965     
3966     
3967 });
3968
3969  
3970
3971  /*
3972  * - LGPL
3973  *
3974  * element
3975  * 
3976  */
3977
3978 /**
3979  * @class Roo.bootstrap.Element
3980  * @extends Roo.bootstrap.Component
3981  * Bootstrap Element class
3982  * @cfg {String} html contents of the element
3983  * @cfg {String} tag tag of the element
3984  * @cfg {String} cls class of the element
3985  * 
3986  * @constructor
3987  * Create a new Element
3988  * @param {Object} config The config object
3989  */
3990
3991 Roo.bootstrap.Element = function(config){
3992     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3993 };
3994
3995 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3996     
3997     tag: 'div',
3998     cls: '',
3999     html: '',
4000      
4001     
4002     getAutoCreate : function(){
4003         
4004         var cfg = {
4005             tag: this.tag,
4006             cls: this.cls,
4007             html: this.html
4008         }
4009         
4010         
4011         
4012         return cfg;
4013     }
4014    
4015 });
4016
4017  
4018
4019  /*
4020  * - LGPL
4021  *
4022  * pagination
4023  * 
4024  */
4025
4026 /**
4027  * @class Roo.bootstrap.Pagination
4028  * @extends Roo.bootstrap.Component
4029  * Bootstrap Pagination class
4030  * @cfg {String} size xs | sm | md | lg
4031  * @cfg {Boolean} inverse false | true
4032  * 
4033  * @constructor
4034  * Create a new Pagination
4035  * @param {Object} config The config object
4036  */
4037
4038 Roo.bootstrap.Pagination = function(config){
4039     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4040 };
4041
4042 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4043     
4044     cls: false,
4045     size: false,
4046     inverse: false,
4047     
4048     getAutoCreate : function(){
4049         var cfg = {
4050             tag: 'ul',
4051                 cls: 'pagination'
4052         };
4053         if (this.inverse) {
4054             cfg.cls += ' inverse';
4055         }
4056         if (this.html) {
4057             cfg.html=this.html;
4058         }
4059         if (this.cls) {
4060             cfg.cls += " " + this.cls;
4061         }
4062         return cfg;
4063     }
4064    
4065 });
4066
4067  
4068
4069  /*
4070  * - LGPL
4071  *
4072  * Pagination item
4073  * 
4074  */
4075
4076
4077 /**
4078  * @class Roo.bootstrap.PaginationItem
4079  * @extends Roo.bootstrap.Component
4080  * Bootstrap PaginationItem class
4081  * @cfg {String} html text
4082  * @cfg {String} href the link
4083  * @cfg {Boolean} preventDefault (true | false) default true
4084  * @cfg {Boolean} active (true | false) default false
4085  * 
4086  * 
4087  * @constructor
4088  * Create a new PaginationItem
4089  * @param {Object} config The config object
4090  */
4091
4092
4093 Roo.bootstrap.PaginationItem = function(config){
4094     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4095     this.addEvents({
4096         // raw events
4097         /**
4098          * @event click
4099          * The raw click event for the entire grid.
4100          * @param {Roo.EventObject} e
4101          */
4102         "click" : true
4103     });
4104 };
4105
4106 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4107     
4108     href : false,
4109     html : false,
4110     preventDefault: true,
4111     active : false,
4112     cls : false,
4113     
4114     getAutoCreate : function(){
4115         var cfg= {
4116             tag: 'li',
4117             cn: [
4118                 {
4119                     tag : 'a',
4120                     href : this.href ? this.href : '#',
4121                     html : this.html ? this.html : ''
4122                 }
4123             ]
4124         };
4125         
4126         if(this.cls){
4127             cfg.cls = this.cls;
4128         }
4129         
4130         if(this.active){
4131             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4132         }
4133         
4134         return cfg;
4135     },
4136     
4137     initEvents: function() {
4138         
4139         this.el.on('click', this.onClick, this);
4140         
4141     },
4142     onClick : function(e)
4143     {
4144         Roo.log('PaginationItem on click ');
4145         if(this.preventDefault){
4146             e.preventDefault();
4147         }
4148         
4149         this.fireEvent('click', this, e);
4150     }
4151    
4152 });
4153
4154  
4155
4156  /*
4157  * - LGPL
4158  *
4159  * slider
4160  * 
4161  */
4162
4163
4164 /**
4165  * @class Roo.bootstrap.Slider
4166  * @extends Roo.bootstrap.Component
4167  * Bootstrap Slider class
4168  *    
4169  * @constructor
4170  * Create a new Slider
4171  * @param {Object} config The config object
4172  */
4173
4174 Roo.bootstrap.Slider = function(config){
4175     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4176 };
4177
4178 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4179     
4180     getAutoCreate : function(){
4181         
4182         var cfg = {
4183             tag: 'div',
4184             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4185             cn: [
4186                 {
4187                     tag: 'a',
4188                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4189                 }
4190             ]
4191         }
4192         
4193         return cfg;
4194     }
4195    
4196 });
4197
4198  /*
4199  * Based on:
4200  * Ext JS Library 1.1.1
4201  * Copyright(c) 2006-2007, Ext JS, LLC.
4202  *
4203  * Originally Released Under LGPL - original licence link has changed is not relivant.
4204  *
4205  * Fork - LGPL
4206  * <script type="text/javascript">
4207  */
4208  
4209
4210 /**
4211  * @class Roo.grid.ColumnModel
4212  * @extends Roo.util.Observable
4213  * This is the default implementation of a ColumnModel used by the Grid. It defines
4214  * the columns in the grid.
4215  * <br>Usage:<br>
4216  <pre><code>
4217  var colModel = new Roo.grid.ColumnModel([
4218         {header: "Ticker", width: 60, sortable: true, locked: true},
4219         {header: "Company Name", width: 150, sortable: true},
4220         {header: "Market Cap.", width: 100, sortable: true},
4221         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4222         {header: "Employees", width: 100, sortable: true, resizable: false}
4223  ]);
4224  </code></pre>
4225  * <p>
4226  
4227  * The config options listed for this class are options which may appear in each
4228  * individual column definition.
4229  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4230  * @constructor
4231  * @param {Object} config An Array of column config objects. See this class's
4232  * config objects for details.
4233 */
4234 Roo.grid.ColumnModel = function(config){
4235         /**
4236      * The config passed into the constructor
4237      */
4238     this.config = config;
4239     this.lookup = {};
4240
4241     // if no id, create one
4242     // if the column does not have a dataIndex mapping,
4243     // map it to the order it is in the config
4244     for(var i = 0, len = config.length; i < len; i++){
4245         var c = config[i];
4246         if(typeof c.dataIndex == "undefined"){
4247             c.dataIndex = i;
4248         }
4249         if(typeof c.renderer == "string"){
4250             c.renderer = Roo.util.Format[c.renderer];
4251         }
4252         if(typeof c.id == "undefined"){
4253             c.id = Roo.id();
4254         }
4255         if(c.editor && c.editor.xtype){
4256             c.editor  = Roo.factory(c.editor, Roo.grid);
4257         }
4258         if(c.editor && c.editor.isFormField){
4259             c.editor = new Roo.grid.GridEditor(c.editor);
4260         }
4261         this.lookup[c.id] = c;
4262     }
4263
4264     /**
4265      * The width of columns which have no width specified (defaults to 100)
4266      * @type Number
4267      */
4268     this.defaultWidth = 100;
4269
4270     /**
4271      * Default sortable of columns which have no sortable specified (defaults to false)
4272      * @type Boolean
4273      */
4274     this.defaultSortable = false;
4275
4276     this.addEvents({
4277         /**
4278              * @event widthchange
4279              * Fires when the width of a column changes.
4280              * @param {ColumnModel} this
4281              * @param {Number} columnIndex The column index
4282              * @param {Number} newWidth The new width
4283              */
4284             "widthchange": true,
4285         /**
4286              * @event headerchange
4287              * Fires when the text of a header changes.
4288              * @param {ColumnModel} this
4289              * @param {Number} columnIndex The column index
4290              * @param {Number} newText The new header text
4291              */
4292             "headerchange": true,
4293         /**
4294              * @event hiddenchange
4295              * Fires when a column is hidden or "unhidden".
4296              * @param {ColumnModel} this
4297              * @param {Number} columnIndex The column index
4298              * @param {Boolean} hidden true if hidden, false otherwise
4299              */
4300             "hiddenchange": true,
4301             /**
4302          * @event columnmoved
4303          * Fires when a column is moved.
4304          * @param {ColumnModel} this
4305          * @param {Number} oldIndex
4306          * @param {Number} newIndex
4307          */
4308         "columnmoved" : true,
4309         /**
4310          * @event columlockchange
4311          * Fires when a column's locked state is changed
4312          * @param {ColumnModel} this
4313          * @param {Number} colIndex
4314          * @param {Boolean} locked true if locked
4315          */
4316         "columnlockchange" : true
4317     });
4318     Roo.grid.ColumnModel.superclass.constructor.call(this);
4319 };
4320 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4321     /**
4322      * @cfg {String} header The header text to display in the Grid view.
4323      */
4324     /**
4325      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4326      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4327      * specified, the column's index is used as an index into the Record's data Array.
4328      */
4329     /**
4330      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4331      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4332      */
4333     /**
4334      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4335      * Defaults to the value of the {@link #defaultSortable} property.
4336      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4337      */
4338     /**
4339      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4340      */
4341     /**
4342      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4343      */
4344     /**
4345      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4346      */
4347     /**
4348      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4349      */
4350     /**
4351      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4352      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4353      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4354      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4355      */
4356        /**
4357      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4358      */
4359     /**
4360      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4361      */
4362
4363     /**
4364      * Returns the id of the column at the specified index.
4365      * @param {Number} index The column index
4366      * @return {String} the id
4367      */
4368     getColumnId : function(index){
4369         return this.config[index].id;
4370     },
4371
4372     /**
4373      * Returns the column for a specified id.
4374      * @param {String} id The column id
4375      * @return {Object} the column
4376      */
4377     getColumnById : function(id){
4378         return this.lookup[id];
4379     },
4380
4381     
4382     /**
4383      * Returns the column for a specified dataIndex.
4384      * @param {String} dataIndex The column dataIndex
4385      * @return {Object|Boolean} the column or false if not found
4386      */
4387     getColumnByDataIndex: function(dataIndex){
4388         var index = this.findColumnIndex(dataIndex);
4389         return index > -1 ? this.config[index] : false;
4390     },
4391     
4392     /**
4393      * Returns the index for a specified column id.
4394      * @param {String} id The column id
4395      * @return {Number} the index, or -1 if not found
4396      */
4397     getIndexById : function(id){
4398         for(var i = 0, len = this.config.length; i < len; i++){
4399             if(this.config[i].id == id){
4400                 return i;
4401             }
4402         }
4403         return -1;
4404     },
4405     
4406     /**
4407      * Returns the index for a specified column dataIndex.
4408      * @param {String} dataIndex The column dataIndex
4409      * @return {Number} the index, or -1 if not found
4410      */
4411     
4412     findColumnIndex : function(dataIndex){
4413         for(var i = 0, len = this.config.length; i < len; i++){
4414             if(this.config[i].dataIndex == dataIndex){
4415                 return i;
4416             }
4417         }
4418         return -1;
4419     },
4420     
4421     
4422     moveColumn : function(oldIndex, newIndex){
4423         var c = this.config[oldIndex];
4424         this.config.splice(oldIndex, 1);
4425         this.config.splice(newIndex, 0, c);
4426         this.dataMap = null;
4427         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4428     },
4429
4430     isLocked : function(colIndex){
4431         return this.config[colIndex].locked === true;
4432     },
4433
4434     setLocked : function(colIndex, value, suppressEvent){
4435         if(this.isLocked(colIndex) == value){
4436             return;
4437         }
4438         this.config[colIndex].locked = value;
4439         if(!suppressEvent){
4440             this.fireEvent("columnlockchange", this, colIndex, value);
4441         }
4442     },
4443
4444     getTotalLockedWidth : function(){
4445         var totalWidth = 0;
4446         for(var i = 0; i < this.config.length; i++){
4447             if(this.isLocked(i) && !this.isHidden(i)){
4448                 this.totalWidth += this.getColumnWidth(i);
4449             }
4450         }
4451         return totalWidth;
4452     },
4453
4454     getLockedCount : function(){
4455         for(var i = 0, len = this.config.length; i < len; i++){
4456             if(!this.isLocked(i)){
4457                 return i;
4458             }
4459         }
4460     },
4461
4462     /**
4463      * Returns the number of columns.
4464      * @return {Number}
4465      */
4466     getColumnCount : function(visibleOnly){
4467         if(visibleOnly === true){
4468             var c = 0;
4469             for(var i = 0, len = this.config.length; i < len; i++){
4470                 if(!this.isHidden(i)){
4471                     c++;
4472                 }
4473             }
4474             return c;
4475         }
4476         return this.config.length;
4477     },
4478
4479     /**
4480      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4481      * @param {Function} fn
4482      * @param {Object} scope (optional)
4483      * @return {Array} result
4484      */
4485     getColumnsBy : function(fn, scope){
4486         var r = [];
4487         for(var i = 0, len = this.config.length; i < len; i++){
4488             var c = this.config[i];
4489             if(fn.call(scope||this, c, i) === true){
4490                 r[r.length] = c;
4491             }
4492         }
4493         return r;
4494     },
4495
4496     /**
4497      * Returns true if the specified column is sortable.
4498      * @param {Number} col The column index
4499      * @return {Boolean}
4500      */
4501     isSortable : function(col){
4502         if(typeof this.config[col].sortable == "undefined"){
4503             return this.defaultSortable;
4504         }
4505         return this.config[col].sortable;
4506     },
4507
4508     /**
4509      * Returns the rendering (formatting) function defined for the column.
4510      * @param {Number} col The column index.
4511      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4512      */
4513     getRenderer : function(col){
4514         if(!this.config[col].renderer){
4515             return Roo.grid.ColumnModel.defaultRenderer;
4516         }
4517         return this.config[col].renderer;
4518     },
4519
4520     /**
4521      * Sets the rendering (formatting) function for a column.
4522      * @param {Number} col The column index
4523      * @param {Function} fn The function to use to process the cell's raw data
4524      * to return HTML markup for the grid view. The render function is called with
4525      * the following parameters:<ul>
4526      * <li>Data value.</li>
4527      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4528      * <li>css A CSS style string to apply to the table cell.</li>
4529      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4530      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4531      * <li>Row index</li>
4532      * <li>Column index</li>
4533      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4534      */
4535     setRenderer : function(col, fn){
4536         this.config[col].renderer = fn;
4537     },
4538
4539     /**
4540      * Returns the width for the specified column.
4541      * @param {Number} col The column index
4542      * @return {Number}
4543      */
4544     getColumnWidth : function(col){
4545         return this.config[col].width * 1 || this.defaultWidth;
4546     },
4547
4548     /**
4549      * Sets the width for a column.
4550      * @param {Number} col The column index
4551      * @param {Number} width The new width
4552      */
4553     setColumnWidth : function(col, width, suppressEvent){
4554         this.config[col].width = width;
4555         this.totalWidth = null;
4556         if(!suppressEvent){
4557              this.fireEvent("widthchange", this, col, width);
4558         }
4559     },
4560
4561     /**
4562      * Returns the total width of all columns.
4563      * @param {Boolean} includeHidden True to include hidden column widths
4564      * @return {Number}
4565      */
4566     getTotalWidth : function(includeHidden){
4567         if(!this.totalWidth){
4568             this.totalWidth = 0;
4569             for(var i = 0, len = this.config.length; i < len; i++){
4570                 if(includeHidden || !this.isHidden(i)){
4571                     this.totalWidth += this.getColumnWidth(i);
4572                 }
4573             }
4574         }
4575         return this.totalWidth;
4576     },
4577
4578     /**
4579      * Returns the header for the specified column.
4580      * @param {Number} col The column index
4581      * @return {String}
4582      */
4583     getColumnHeader : function(col){
4584         return this.config[col].header;
4585     },
4586
4587     /**
4588      * Sets the header for a column.
4589      * @param {Number} col The column index
4590      * @param {String} header The new header
4591      */
4592     setColumnHeader : function(col, header){
4593         this.config[col].header = header;
4594         this.fireEvent("headerchange", this, col, header);
4595     },
4596
4597     /**
4598      * Returns the tooltip for the specified column.
4599      * @param {Number} col The column index
4600      * @return {String}
4601      */
4602     getColumnTooltip : function(col){
4603             return this.config[col].tooltip;
4604     },
4605     /**
4606      * Sets the tooltip for a column.
4607      * @param {Number} col The column index
4608      * @param {String} tooltip The new tooltip
4609      */
4610     setColumnTooltip : function(col, tooltip){
4611             this.config[col].tooltip = tooltip;
4612     },
4613
4614     /**
4615      * Returns the dataIndex for the specified column.
4616      * @param {Number} col The column index
4617      * @return {Number}
4618      */
4619     getDataIndex : function(col){
4620         return this.config[col].dataIndex;
4621     },
4622
4623     /**
4624      * Sets the dataIndex for a column.
4625      * @param {Number} col The column index
4626      * @param {Number} dataIndex The new dataIndex
4627      */
4628     setDataIndex : function(col, dataIndex){
4629         this.config[col].dataIndex = dataIndex;
4630     },
4631
4632     
4633     
4634     /**
4635      * Returns true if the cell is editable.
4636      * @param {Number} colIndex The column index
4637      * @param {Number} rowIndex The row index
4638      * @return {Boolean}
4639      */
4640     isCellEditable : function(colIndex, rowIndex){
4641         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4642     },
4643
4644     /**
4645      * Returns the editor defined for the cell/column.
4646      * return false or null to disable editing.
4647      * @param {Number} colIndex The column index
4648      * @param {Number} rowIndex The row index
4649      * @return {Object}
4650      */
4651     getCellEditor : function(colIndex, rowIndex){
4652         return this.config[colIndex].editor;
4653     },
4654
4655     /**
4656      * Sets if a column is editable.
4657      * @param {Number} col The column index
4658      * @param {Boolean} editable True if the column is editable
4659      */
4660     setEditable : function(col, editable){
4661         this.config[col].editable = editable;
4662     },
4663
4664
4665     /**
4666      * Returns true if the column is hidden.
4667      * @param {Number} colIndex The column index
4668      * @return {Boolean}
4669      */
4670     isHidden : function(colIndex){
4671         return this.config[colIndex].hidden;
4672     },
4673
4674
4675     /**
4676      * Returns true if the column width cannot be changed
4677      */
4678     isFixed : function(colIndex){
4679         return this.config[colIndex].fixed;
4680     },
4681
4682     /**
4683      * Returns true if the column can be resized
4684      * @return {Boolean}
4685      */
4686     isResizable : function(colIndex){
4687         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4688     },
4689     /**
4690      * Sets if a column is hidden.
4691      * @param {Number} colIndex The column index
4692      * @param {Boolean} hidden True if the column is hidden
4693      */
4694     setHidden : function(colIndex, hidden){
4695         this.config[colIndex].hidden = hidden;
4696         this.totalWidth = null;
4697         this.fireEvent("hiddenchange", this, colIndex, hidden);
4698     },
4699
4700     /**
4701      * Sets the editor for a column.
4702      * @param {Number} col The column index
4703      * @param {Object} editor The editor object
4704      */
4705     setEditor : function(col, editor){
4706         this.config[col].editor = editor;
4707     }
4708 });
4709
4710 Roo.grid.ColumnModel.defaultRenderer = function(value){
4711         if(typeof value == "string" && value.length < 1){
4712             return "&#160;";
4713         }
4714         return value;
4715 };
4716
4717 // Alias for backwards compatibility
4718 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4719 /*
4720  * Based on:
4721  * Ext JS Library 1.1.1
4722  * Copyright(c) 2006-2007, Ext JS, LLC.
4723  *
4724  * Originally Released Under LGPL - original licence link has changed is not relivant.
4725  *
4726  * Fork - LGPL
4727  * <script type="text/javascript">
4728  */
4729  
4730 /**
4731  * @class Roo.LoadMask
4732  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4733  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4734  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4735  * element's UpdateManager load indicator and will be destroyed after the initial load.
4736  * @constructor
4737  * Create a new LoadMask
4738  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4739  * @param {Object} config The config object
4740  */
4741 Roo.LoadMask = function(el, config){
4742     this.el = Roo.get(el);
4743     Roo.apply(this, config);
4744     if(this.store){
4745         this.store.on('beforeload', this.onBeforeLoad, this);
4746         this.store.on('load', this.onLoad, this);
4747         this.store.on('loadexception', this.onLoadException, this);
4748         this.removeMask = false;
4749     }else{
4750         var um = this.el.getUpdateManager();
4751         um.showLoadIndicator = false; // disable the default indicator
4752         um.on('beforeupdate', this.onBeforeLoad, this);
4753         um.on('update', this.onLoad, this);
4754         um.on('failure', this.onLoad, this);
4755         this.removeMask = true;
4756     }
4757 };
4758
4759 Roo.LoadMask.prototype = {
4760     /**
4761      * @cfg {Boolean} removeMask
4762      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4763      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4764      */
4765     /**
4766      * @cfg {String} msg
4767      * The text to display in a centered loading message box (defaults to 'Loading...')
4768      */
4769     msg : 'Loading...',
4770     /**
4771      * @cfg {String} msgCls
4772      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4773      */
4774     msgCls : 'x-mask-loading',
4775
4776     /**
4777      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4778      * @type Boolean
4779      */
4780     disabled: false,
4781
4782     /**
4783      * Disables the mask to prevent it from being displayed
4784      */
4785     disable : function(){
4786        this.disabled = true;
4787     },
4788
4789     /**
4790      * Enables the mask so that it can be displayed
4791      */
4792     enable : function(){
4793         this.disabled = false;
4794     },
4795     
4796     onLoadException : function()
4797     {
4798         Roo.log(arguments);
4799         
4800         if (typeof(arguments[3]) != 'undefined') {
4801             Roo.MessageBox.alert("Error loading",arguments[3]);
4802         } 
4803         /*
4804         try {
4805             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4806                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4807             }   
4808         } catch(e) {
4809             
4810         }
4811         */
4812     
4813         
4814         
4815         this.el.unmask(this.removeMask);
4816     },
4817     // private
4818     onLoad : function()
4819     {
4820         this.el.unmask(this.removeMask);
4821     },
4822
4823     // private
4824     onBeforeLoad : function(){
4825         if(!this.disabled){
4826             this.el.mask(this.msg, this.msgCls);
4827         }
4828     },
4829
4830     // private
4831     destroy : function(){
4832         if(this.store){
4833             this.store.un('beforeload', this.onBeforeLoad, this);
4834             this.store.un('load', this.onLoad, this);
4835             this.store.un('loadexception', this.onLoadException, this);
4836         }else{
4837             var um = this.el.getUpdateManager();
4838             um.un('beforeupdate', this.onBeforeLoad, this);
4839             um.un('update', this.onLoad, this);
4840             um.un('failure', this.onLoad, this);
4841         }
4842     }
4843 };/*
4844  * - LGPL
4845  *
4846  * table
4847  * 
4848  */
4849
4850 /**
4851  * @class Roo.bootstrap.Table
4852  * @extends Roo.bootstrap.Component
4853  * Bootstrap Table class
4854  * @cfg {String} cls table class
4855  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4856  * @cfg {String} bgcolor Specifies the background color for a table
4857  * @cfg {Number} border Specifies whether the table cells should have borders or not
4858  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4859  * @cfg {Number} cellspacing Specifies the space between cells
4860  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4861  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4862  * @cfg {String} sortable Specifies that the table should be sortable
4863  * @cfg {String} summary Specifies a summary of the content of a table
4864  * @cfg {Number} width Specifies the width of a table
4865  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4866  * 
4867  * @cfg {boolean} striped Should the rows be alternative striped
4868  * @cfg {boolean} bordered Add borders to the table
4869  * @cfg {boolean} hover Add hover highlighting
4870  * @cfg {boolean} condensed Format condensed
4871  * @cfg {boolean} responsive Format condensed
4872  * @cfg {Boolean} loadMask (true|false) default false
4873  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4874  * @cfg {Boolean} thead (true|false) generate thead, default true
4875  * @cfg {Boolean} RowSelection (true|false) default false
4876  * @cfg {Boolean} CellSelection (true|false) default false
4877  *
4878  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4879  
4880  * 
4881  * @constructor
4882  * Create a new Table
4883  * @param {Object} config The config object
4884  */
4885
4886 Roo.bootstrap.Table = function(config){
4887     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4888     
4889     if (this.sm) {
4890         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4891         this.sm = this.selModel;
4892         this.sm.xmodule = this.xmodule || false;
4893     }
4894     if (this.cm && typeof(this.cm.config) == 'undefined') {
4895         this.colModel = new Roo.grid.ColumnModel(this.cm);
4896         this.cm = this.colModel;
4897         this.cm.xmodule = this.xmodule || false;
4898     }
4899     if (this.store) {
4900         this.store= Roo.factory(this.store, Roo.data);
4901         this.ds = this.store;
4902         this.ds.xmodule = this.xmodule || false;
4903          
4904     }
4905     if (this.footer && this.store) {
4906         this.footer.dataSource = this.ds;
4907         this.footer = Roo.factory(this.footer);
4908     }
4909     
4910     /** @private */
4911     this.addEvents({
4912         /**
4913          * @event cellclick
4914          * Fires when a cell is clicked
4915          * @param {Roo.bootstrap.Table} this
4916          * @param {Roo.Element} el
4917          * @param {Number} rowIndex
4918          * @param {Number} columnIndex
4919          * @param {Roo.EventObject} e
4920          */
4921         "cellclick" : true,
4922         /**
4923          * @event celldblclick
4924          * Fires when a cell is double clicked
4925          * @param {Roo.bootstrap.Table} this
4926          * @param {Roo.Element} el
4927          * @param {Number} rowIndex
4928          * @param {Number} columnIndex
4929          * @param {Roo.EventObject} e
4930          */
4931         "celldblclick" : true,
4932         /**
4933          * @event rowclick
4934          * Fires when a row is clicked
4935          * @param {Roo.bootstrap.Table} this
4936          * @param {Roo.Element} el
4937          * @param {Number} rowIndex
4938          * @param {Roo.EventObject} e
4939          */
4940         "rowclick" : true,
4941         /**
4942          * @event rowdblclick
4943          * Fires when a row is double clicked
4944          * @param {Roo.bootstrap.Table} this
4945          * @param {Roo.Element} el
4946          * @param {Number} rowIndex
4947          * @param {Roo.EventObject} e
4948          */
4949         "rowdblclick" : true,
4950         /**
4951          * @event mouseover
4952          * Fires when a mouseover occur
4953          * @param {Roo.bootstrap.Table} this
4954          * @param {Roo.Element} el
4955          * @param {Number} rowIndex
4956          * @param {Number} columnIndex
4957          * @param {Roo.EventObject} e
4958          */
4959         "mouseover" : true,
4960         /**
4961          * @event mouseout
4962          * Fires when a mouseout occur
4963          * @param {Roo.bootstrap.Table} this
4964          * @param {Roo.Element} el
4965          * @param {Number} rowIndex
4966          * @param {Number} columnIndex
4967          * @param {Roo.EventObject} e
4968          */
4969         "mouseout" : true,
4970         /**
4971          * @event rowclass
4972          * Fires when a row is rendered, so you can change add a style to it.
4973          * @param {Roo.bootstrap.Table} this
4974          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4975          */
4976         'rowclass' : true
4977         
4978     });
4979 };
4980
4981 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4982     
4983     cls: false,
4984     align: false,
4985     bgcolor: false,
4986     border: false,
4987     cellpadding: false,
4988     cellspacing: false,
4989     frame: false,
4990     rules: false,
4991     sortable: false,
4992     summary: false,
4993     width: false,
4994     striped : false,
4995     bordered: false,
4996     hover:  false,
4997     condensed : false,
4998     responsive : false,
4999     sm : false,
5000     cm : false,
5001     store : false,
5002     loadMask : false,
5003     tfoot : true,
5004     thead : true,
5005     RowSelection : false,
5006     CellSelection : false,
5007     layout : false,
5008     
5009     // Roo.Element - the tbody
5010     mainBody: false, 
5011     
5012     getAutoCreate : function(){
5013         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5014         
5015         cfg = {
5016             tag: 'table',
5017             cls : 'table',
5018             cn : []
5019         }
5020             
5021         if (this.striped) {
5022             cfg.cls += ' table-striped';
5023         }
5024         
5025         if (this.hover) {
5026             cfg.cls += ' table-hover';
5027         }
5028         if (this.bordered) {
5029             cfg.cls += ' table-bordered';
5030         }
5031         if (this.condensed) {
5032             cfg.cls += ' table-condensed';
5033         }
5034         if (this.responsive) {
5035             cfg.cls += ' table-responsive';
5036         }
5037         
5038         if (this.cls) {
5039             cfg.cls+=  ' ' +this.cls;
5040         }
5041         
5042         // this lot should be simplifed...
5043         
5044         if (this.align) {
5045             cfg.align=this.align;
5046         }
5047         if (this.bgcolor) {
5048             cfg.bgcolor=this.bgcolor;
5049         }
5050         if (this.border) {
5051             cfg.border=this.border;
5052         }
5053         if (this.cellpadding) {
5054             cfg.cellpadding=this.cellpadding;
5055         }
5056         if (this.cellspacing) {
5057             cfg.cellspacing=this.cellspacing;
5058         }
5059         if (this.frame) {
5060             cfg.frame=this.frame;
5061         }
5062         if (this.rules) {
5063             cfg.rules=this.rules;
5064         }
5065         if (this.sortable) {
5066             cfg.sortable=this.sortable;
5067         }
5068         if (this.summary) {
5069             cfg.summary=this.summary;
5070         }
5071         if (this.width) {
5072             cfg.width=this.width;
5073         }
5074         if (this.layout) {
5075             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5076         }
5077         
5078         if(this.store || this.cm){
5079             if(this.thead){
5080                 cfg.cn.push(this.renderHeader());
5081             }
5082             
5083             cfg.cn.push(this.renderBody());
5084             
5085             if(this.tfoot){
5086                 cfg.cn.push(this.renderFooter());
5087             }
5088             
5089             cfg.cls+=  ' TableGrid';
5090         }
5091         
5092         return { cn : [ cfg ] };
5093     },
5094     
5095     initEvents : function()
5096     {   
5097         if(!this.store || !this.cm){
5098             return;
5099         }
5100         
5101         //Roo.log('initEvents with ds!!!!');
5102         
5103         this.mainBody = this.el.select('tbody', true).first();
5104         
5105         
5106         var _this = this;
5107         
5108         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5109             e.on('click', _this.sort, _this);
5110         });
5111         
5112         this.el.on("click", this.onClick, this);
5113         this.el.on("dblclick", this.onDblClick, this);
5114         
5115         this.parent().el.setStyle('position', 'relative');
5116         if (this.footer) {
5117             this.footer.parentId = this.id;
5118             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5119         }
5120         
5121         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5122         
5123         this.store.on('load', this.onLoad, this);
5124         this.store.on('beforeload', this.onBeforeLoad, this);
5125         this.store.on('update', this.onUpdate, this);
5126         
5127     },
5128     
5129     onMouseover : function(e, el)
5130     {
5131         var cell = Roo.get(el);
5132         
5133         if(!cell){
5134             return;
5135         }
5136         
5137         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5138             cell = cell.findParent('td', false, true);
5139         }
5140         
5141         var row = cell.findParent('tr', false, true);
5142         var cellIndex = cell.dom.cellIndex;
5143         var rowIndex = row.dom.rowIndex - 1; // start from 0
5144         
5145         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5146         
5147     },
5148     
5149     onMouseout : function(e, el)
5150     {
5151         var cell = Roo.get(el);
5152         
5153         if(!cell){
5154             return;
5155         }
5156         
5157         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5158             cell = cell.findParent('td', false, true);
5159         }
5160         
5161         var row = cell.findParent('tr', false, true);
5162         var cellIndex = cell.dom.cellIndex;
5163         var rowIndex = row.dom.rowIndex - 1; // start from 0
5164         
5165         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5166         
5167     },
5168     
5169     onClick : function(e, el)
5170     {
5171         var cell = Roo.get(el);
5172         
5173         if(!cell || (!this.CellSelection && !this.RowSelection)){
5174             return;
5175         }
5176         
5177         
5178         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5179             cell = cell.findParent('td', false, true);
5180         }
5181         
5182         var row = cell.findParent('tr', false, true);
5183         var cellIndex = cell.dom.cellIndex;
5184         var rowIndex = row.dom.rowIndex - 1;
5185         
5186         if(this.CellSelection){
5187             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5188         }
5189         
5190         if(this.RowSelection){
5191             this.fireEvent('rowclick', this, row, rowIndex, e);
5192         }
5193         
5194         
5195     },
5196     
5197     onDblClick : function(e,el)
5198     {
5199         var cell = Roo.get(el);
5200         
5201         if(!cell || (!this.CellSelection && !this.RowSelection)){
5202             return;
5203         }
5204         
5205         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5206             cell = cell.findParent('td', false, true);
5207         }
5208         
5209         var row = cell.findParent('tr', false, true);
5210         var cellIndex = cell.dom.cellIndex;
5211         var rowIndex = row.dom.rowIndex - 1;
5212         
5213         if(this.CellSelection){
5214             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5215         }
5216         
5217         if(this.RowSelection){
5218             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5219         }
5220     },
5221     
5222     sort : function(e,el)
5223     {
5224         var col = Roo.get(el)
5225         
5226         if(!col.hasClass('sortable')){
5227             return;
5228         }
5229         
5230         var sort = col.attr('sort');
5231         var dir = 'ASC';
5232         
5233         if(col.hasClass('glyphicon-arrow-up')){
5234             dir = 'DESC';
5235         }
5236         
5237         this.store.sortInfo = {field : sort, direction : dir};
5238         
5239         if (this.footer) {
5240             Roo.log("calling footer first");
5241             this.footer.onClick('first');
5242         } else {
5243         
5244             this.store.load({ params : { start : 0 } });
5245         }
5246     },
5247     
5248     renderHeader : function()
5249     {
5250         var header = {
5251             tag: 'thead',
5252             cn : []
5253         };
5254         
5255         var cm = this.cm;
5256         
5257         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5258             
5259             var config = cm.config[i];
5260                     
5261             var c = {
5262                 tag: 'th',
5263                 style : '',
5264                 html: cm.getColumnHeader(i)
5265             };
5266             
5267             if(typeof(config.hidden) != 'undefined' && config.hidden){
5268                 c.style += ' display:none;';
5269             }
5270             
5271             if(typeof(config.dataIndex) != 'undefined'){
5272                 c.sort = config.dataIndex;
5273             }
5274             
5275             if(typeof(config.sortable) != 'undefined' && config.sortable){
5276                 c.cls = 'sortable';
5277             }
5278             
5279             if(typeof(config.align) != 'undefined' && config.align.length){
5280                 c.style += ' text-align:' + config.align + ';';
5281             }
5282             
5283             if(typeof(config.width) != 'undefined'){
5284                 c.style += ' width:' + config.width + 'px;';
5285             }
5286             
5287             header.cn.push(c)
5288         }
5289         
5290         return header;
5291     },
5292     
5293     renderBody : function()
5294     {
5295         var body = {
5296             tag: 'tbody',
5297             cn : [
5298                 {
5299                     tag: 'tr',
5300                     cn : [
5301                         {
5302                             tag : 'td',
5303                             colspan :  this.cm.getColumnCount()
5304                         }
5305                     ]
5306                 }
5307             ]
5308         };
5309         
5310         return body;
5311     },
5312     
5313     renderFooter : function()
5314     {
5315         var footer = {
5316             tag: 'tfoot',
5317             cn : [
5318                 {
5319                     tag: 'tr',
5320                     cn : [
5321                         {
5322                             tag : 'td',
5323                             colspan :  this.cm.getColumnCount()
5324                         }
5325                     ]
5326                 }
5327             ]
5328         };
5329         
5330         return footer;
5331     },
5332     
5333     
5334     
5335     onLoad : function()
5336     {
5337         Roo.log('ds onload');
5338         this.clear();
5339         
5340         var _this = this;
5341         var cm = this.cm;
5342         var ds = this.store;
5343         
5344         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5345             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5346             
5347             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5348                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5349             }
5350             
5351             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5352                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5353             }
5354         });
5355         
5356         var tbody =  this.mainBody;
5357               
5358         if(ds.getCount() > 0){
5359             ds.data.each(function(d,rowIndex){
5360                 var row =  this.renderRow(cm, ds, rowIndex);
5361                 
5362                 tbody.createChild(row);
5363                 
5364                 var _this = this;
5365                 
5366                 if(row.cellObjects.length){
5367                     Roo.each(row.cellObjects, function(r){
5368                         _this.renderCellObject(r);
5369                     })
5370                 }
5371                 
5372             }, this);
5373         }
5374         
5375         Roo.each(this.el.select('tbody td', true).elements, function(e){
5376             e.on('mouseover', _this.onMouseover, _this);
5377         });
5378         
5379         Roo.each(this.el.select('tbody td', true).elements, function(e){
5380             e.on('mouseout', _this.onMouseout, _this);
5381         });
5382
5383         //if(this.loadMask){
5384         //    this.maskEl.hide();
5385         //}
5386     },
5387     
5388     
5389     onUpdate : function(ds,record)
5390     {
5391         this.refreshRow(record);
5392     },
5393     onRemove : function(ds, record, index, isUpdate){
5394         if(isUpdate !== true){
5395             this.fireEvent("beforerowremoved", this, index, record);
5396         }
5397         var bt = this.mainBody.dom;
5398         if(bt.rows[index]){
5399             bt.removeChild(bt.rows[index]);
5400         }
5401         
5402         if(isUpdate !== true){
5403             //this.stripeRows(index);
5404             //this.syncRowHeights(index, index);
5405             //this.layout();
5406             this.fireEvent("rowremoved", this, index, record);
5407         }
5408     },
5409     
5410     
5411     refreshRow : function(record){
5412         var ds = this.store, index;
5413         if(typeof record == 'number'){
5414             index = record;
5415             record = ds.getAt(index);
5416         }else{
5417             index = ds.indexOf(record);
5418         }
5419         this.insertRow(ds, index, true);
5420         this.onRemove(ds, record, index+1, true);
5421         //this.syncRowHeights(index, index);
5422         //this.layout();
5423         this.fireEvent("rowupdated", this, index, record);
5424     },
5425     
5426     insertRow : function(dm, rowIndex, isUpdate){
5427         
5428         if(!isUpdate){
5429             this.fireEvent("beforerowsinserted", this, rowIndex);
5430         }
5431             //var s = this.getScrollState();
5432         var row = this.renderRow(this.cm, this.store, rowIndex);
5433         // insert before rowIndex..
5434         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5435         
5436         var _this = this;
5437                 
5438         if(row.cellObjects.length){
5439             Roo.each(row.cellObjects, function(r){
5440                 _this.renderCellObject(r);
5441             })
5442         }
5443             
5444         if(!isUpdate){
5445             this.fireEvent("rowsinserted", this, rowIndex);
5446             //this.syncRowHeights(firstRow, lastRow);
5447             //this.stripeRows(firstRow);
5448             //this.layout();
5449         }
5450         
5451     },
5452     
5453     
5454     getRowDom : function(rowIndex)
5455     {
5456         // not sure if I need to check this.. but let's do it anyway..
5457         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5458                 this.mainBody.dom.rows[rowIndex] : false
5459     },
5460     // returns the object tree for a tr..
5461   
5462     
5463     renderRow : function(cm, ds, rowIndex) {
5464         
5465         var d = ds.getAt(rowIndex);
5466         
5467         var row = {
5468             tag : 'tr',
5469             cn : []
5470         };
5471             
5472         var cellObjects = [];
5473         
5474         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5475             var config = cm.config[i];
5476             
5477             var renderer = cm.getRenderer(i);
5478             var value = '';
5479             var id = false;
5480             
5481             if(typeof(renderer) !== 'undefined'){
5482                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5483             }
5484             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5485             // and are rendered into the cells after the row is rendered - using the id for the element.
5486             
5487             if(typeof(value) === 'object'){
5488                 id = Roo.id();
5489                 cellObjects.push({
5490                     container : id,
5491                     cfg : value 
5492                 })
5493             }
5494             
5495             var rowcfg = {
5496                 record: d,
5497                 rowIndex : rowIndex,
5498                 colIndex : i,
5499                 rowClass : ''
5500             }
5501
5502             this.fireEvent('rowclass', this, rowcfg);
5503             
5504             var td = {
5505                 tag: 'td',
5506                 cls : rowcfg.rowClass,
5507                 style: '',
5508                 html: (typeof(value) === 'object') ? '' : value
5509             };
5510             
5511             if (id) {
5512                 td.id = id;
5513             }
5514             
5515             if(typeof(config.hidden) != 'undefined' && config.hidden){
5516                 td.style += ' display:none;';
5517             }
5518             
5519             if(typeof(config.align) != 'undefined' && config.align.length){
5520                 td.style += ' text-align:' + config.align + ';';
5521             }
5522             
5523             if(typeof(config.width) != 'undefined'){
5524                 td.style += ' width:' +  config.width + 'px;';
5525             }
5526              
5527             row.cn.push(td);
5528            
5529         }
5530         
5531         row.cellObjects = cellObjects;
5532         
5533         return row;
5534           
5535     },
5536     
5537     
5538     
5539     onBeforeLoad : function()
5540     {
5541         //Roo.log('ds onBeforeLoad');
5542         
5543         //this.clear();
5544         
5545         //if(this.loadMask){
5546         //    this.maskEl.show();
5547         //}
5548     },
5549     
5550     clear : function()
5551     {
5552         this.el.select('tbody', true).first().dom.innerHTML = '';
5553     },
5554     
5555     getSelectionModel : function(){
5556         if(!this.selModel){
5557             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5558         }
5559         return this.selModel;
5560     },
5561     /*
5562      * Render the Roo.bootstrap object from renderder
5563      */
5564     renderCellObject : function(r)
5565     {
5566         var _this = this;
5567         
5568         var t = r.cfg.render(r.container);
5569         
5570         if(r.cfg.cn){
5571             Roo.each(r.cfg.cn, function(c){
5572                 var child = {
5573                     container: t.getChildContainer(),
5574                     cfg: c
5575                 }
5576                 _this.renderCellObject(child);
5577             })
5578         }
5579     }
5580    
5581 });
5582
5583  
5584
5585  /*
5586  * - LGPL
5587  *
5588  * table cell
5589  * 
5590  */
5591
5592 /**
5593  * @class Roo.bootstrap.TableCell
5594  * @extends Roo.bootstrap.Component
5595  * Bootstrap TableCell class
5596  * @cfg {String} html cell contain text
5597  * @cfg {String} cls cell class
5598  * @cfg {String} tag cell tag (td|th) default td
5599  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5600  * @cfg {String} align Aligns the content in a cell
5601  * @cfg {String} axis Categorizes cells
5602  * @cfg {String} bgcolor Specifies the background color of a cell
5603  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5604  * @cfg {Number} colspan Specifies the number of columns a cell should span
5605  * @cfg {String} headers Specifies one or more header cells a cell is related to
5606  * @cfg {Number} height Sets the height of a cell
5607  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5608  * @cfg {Number} rowspan Sets the number of rows a cell should span
5609  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5610  * @cfg {String} valign Vertical aligns the content in a cell
5611  * @cfg {Number} width Specifies the width of a cell
5612  * 
5613  * @constructor
5614  * Create a new TableCell
5615  * @param {Object} config The config object
5616  */
5617
5618 Roo.bootstrap.TableCell = function(config){
5619     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5620 };
5621
5622 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5623     
5624     html: false,
5625     cls: false,
5626     tag: false,
5627     abbr: false,
5628     align: false,
5629     axis: false,
5630     bgcolor: false,
5631     charoff: false,
5632     colspan: false,
5633     headers: false,
5634     height: false,
5635     nowrap: false,
5636     rowspan: false,
5637     scope: false,
5638     valign: false,
5639     width: false,
5640     
5641     
5642     getAutoCreate : function(){
5643         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5644         
5645         cfg = {
5646             tag: 'td'
5647         }
5648         
5649         if(this.tag){
5650             cfg.tag = this.tag;
5651         }
5652         
5653         if (this.html) {
5654             cfg.html=this.html
5655         }
5656         if (this.cls) {
5657             cfg.cls=this.cls
5658         }
5659         if (this.abbr) {
5660             cfg.abbr=this.abbr
5661         }
5662         if (this.align) {
5663             cfg.align=this.align
5664         }
5665         if (this.axis) {
5666             cfg.axis=this.axis
5667         }
5668         if (this.bgcolor) {
5669             cfg.bgcolor=this.bgcolor
5670         }
5671         if (this.charoff) {
5672             cfg.charoff=this.charoff
5673         }
5674         if (this.colspan) {
5675             cfg.colspan=this.colspan
5676         }
5677         if (this.headers) {
5678             cfg.headers=this.headers
5679         }
5680         if (this.height) {
5681             cfg.height=this.height
5682         }
5683         if (this.nowrap) {
5684             cfg.nowrap=this.nowrap
5685         }
5686         if (this.rowspan) {
5687             cfg.rowspan=this.rowspan
5688         }
5689         if (this.scope) {
5690             cfg.scope=this.scope
5691         }
5692         if (this.valign) {
5693             cfg.valign=this.valign
5694         }
5695         if (this.width) {
5696             cfg.width=this.width
5697         }
5698         
5699         
5700         return cfg;
5701     }
5702    
5703 });
5704
5705  
5706
5707  /*
5708  * - LGPL
5709  *
5710  * table row
5711  * 
5712  */
5713
5714 /**
5715  * @class Roo.bootstrap.TableRow
5716  * @extends Roo.bootstrap.Component
5717  * Bootstrap TableRow class
5718  * @cfg {String} cls row class
5719  * @cfg {String} align Aligns the content in a table row
5720  * @cfg {String} bgcolor Specifies a background color for a table row
5721  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5722  * @cfg {String} valign Vertical aligns the content in a table row
5723  * 
5724  * @constructor
5725  * Create a new TableRow
5726  * @param {Object} config The config object
5727  */
5728
5729 Roo.bootstrap.TableRow = function(config){
5730     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5731 };
5732
5733 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5734     
5735     cls: false,
5736     align: false,
5737     bgcolor: false,
5738     charoff: false,
5739     valign: false,
5740     
5741     getAutoCreate : function(){
5742         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5743         
5744         cfg = {
5745             tag: 'tr'
5746         }
5747             
5748         if(this.cls){
5749             cfg.cls = this.cls;
5750         }
5751         if(this.align){
5752             cfg.align = this.align;
5753         }
5754         if(this.bgcolor){
5755             cfg.bgcolor = this.bgcolor;
5756         }
5757         if(this.charoff){
5758             cfg.charoff = this.charoff;
5759         }
5760         if(this.valign){
5761             cfg.valign = this.valign;
5762         }
5763         
5764         return cfg;
5765     }
5766    
5767 });
5768
5769  
5770
5771  /*
5772  * - LGPL
5773  *
5774  * table body
5775  * 
5776  */
5777
5778 /**
5779  * @class Roo.bootstrap.TableBody
5780  * @extends Roo.bootstrap.Component
5781  * Bootstrap TableBody class
5782  * @cfg {String} cls element class
5783  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5784  * @cfg {String} align Aligns the content inside the element
5785  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5786  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5787  * 
5788  * @constructor
5789  * Create a new TableBody
5790  * @param {Object} config The config object
5791  */
5792
5793 Roo.bootstrap.TableBody = function(config){
5794     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5795 };
5796
5797 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5798     
5799     cls: false,
5800     tag: false,
5801     align: false,
5802     charoff: false,
5803     valign: false,
5804     
5805     getAutoCreate : function(){
5806         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5807         
5808         cfg = {
5809             tag: 'tbody'
5810         }
5811             
5812         if (this.cls) {
5813             cfg.cls=this.cls
5814         }
5815         if(this.tag){
5816             cfg.tag = this.tag;
5817         }
5818         
5819         if(this.align){
5820             cfg.align = this.align;
5821         }
5822         if(this.charoff){
5823             cfg.charoff = this.charoff;
5824         }
5825         if(this.valign){
5826             cfg.valign = this.valign;
5827         }
5828         
5829         return cfg;
5830     }
5831     
5832     
5833 //    initEvents : function()
5834 //    {
5835 //        
5836 //        if(!this.store){
5837 //            return;
5838 //        }
5839 //        
5840 //        this.store = Roo.factory(this.store, Roo.data);
5841 //        this.store.on('load', this.onLoad, this);
5842 //        
5843 //        this.store.load();
5844 //        
5845 //    },
5846 //    
5847 //    onLoad: function () 
5848 //    {   
5849 //        this.fireEvent('load', this);
5850 //    }
5851 //    
5852 //   
5853 });
5854
5855  
5856
5857  /*
5858  * Based on:
5859  * Ext JS Library 1.1.1
5860  * Copyright(c) 2006-2007, Ext JS, LLC.
5861  *
5862  * Originally Released Under LGPL - original licence link has changed is not relivant.
5863  *
5864  * Fork - LGPL
5865  * <script type="text/javascript">
5866  */
5867
5868 // as we use this in bootstrap.
5869 Roo.namespace('Roo.form');
5870  /**
5871  * @class Roo.form.Action
5872  * Internal Class used to handle form actions
5873  * @constructor
5874  * @param {Roo.form.BasicForm} el The form element or its id
5875  * @param {Object} config Configuration options
5876  */
5877
5878  
5879  
5880 // define the action interface
5881 Roo.form.Action = function(form, options){
5882     this.form = form;
5883     this.options = options || {};
5884 };
5885 /**
5886  * Client Validation Failed
5887  * @const 
5888  */
5889 Roo.form.Action.CLIENT_INVALID = 'client';
5890 /**
5891  * Server Validation Failed
5892  * @const 
5893  */
5894 Roo.form.Action.SERVER_INVALID = 'server';
5895  /**
5896  * Connect to Server Failed
5897  * @const 
5898  */
5899 Roo.form.Action.CONNECT_FAILURE = 'connect';
5900 /**
5901  * Reading Data from Server Failed
5902  * @const 
5903  */
5904 Roo.form.Action.LOAD_FAILURE = 'load';
5905
5906 Roo.form.Action.prototype = {
5907     type : 'default',
5908     failureType : undefined,
5909     response : undefined,
5910     result : undefined,
5911
5912     // interface method
5913     run : function(options){
5914
5915     },
5916
5917     // interface method
5918     success : function(response){
5919
5920     },
5921
5922     // interface method
5923     handleResponse : function(response){
5924
5925     },
5926
5927     // default connection failure
5928     failure : function(response){
5929         
5930         this.response = response;
5931         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5932         this.form.afterAction(this, false);
5933     },
5934
5935     processResponse : function(response){
5936         this.response = response;
5937         if(!response.responseText){
5938             return true;
5939         }
5940         this.result = this.handleResponse(response);
5941         return this.result;
5942     },
5943
5944     // utility functions used internally
5945     getUrl : function(appendParams){
5946         var url = this.options.url || this.form.url || this.form.el.dom.action;
5947         if(appendParams){
5948             var p = this.getParams();
5949             if(p){
5950                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5951             }
5952         }
5953         return url;
5954     },
5955
5956     getMethod : function(){
5957         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5958     },
5959
5960     getParams : function(){
5961         var bp = this.form.baseParams;
5962         var p = this.options.params;
5963         if(p){
5964             if(typeof p == "object"){
5965                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5966             }else if(typeof p == 'string' && bp){
5967                 p += '&' + Roo.urlEncode(bp);
5968             }
5969         }else if(bp){
5970             p = Roo.urlEncode(bp);
5971         }
5972         return p;
5973     },
5974
5975     createCallback : function(){
5976         return {
5977             success: this.success,
5978             failure: this.failure,
5979             scope: this,
5980             timeout: (this.form.timeout*1000),
5981             upload: this.form.fileUpload ? this.success : undefined
5982         };
5983     }
5984 };
5985
5986 Roo.form.Action.Submit = function(form, options){
5987     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5988 };
5989
5990 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5991     type : 'submit',
5992
5993     haveProgress : false,
5994     uploadComplete : false,
5995     
5996     // uploadProgress indicator.
5997     uploadProgress : function()
5998     {
5999         if (!this.form.progressUrl) {
6000             return;
6001         }
6002         
6003         if (!this.haveProgress) {
6004             Roo.MessageBox.progress("Uploading", "Uploading");
6005         }
6006         if (this.uploadComplete) {
6007            Roo.MessageBox.hide();
6008            return;
6009         }
6010         
6011         this.haveProgress = true;
6012    
6013         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6014         
6015         var c = new Roo.data.Connection();
6016         c.request({
6017             url : this.form.progressUrl,
6018             params: {
6019                 id : uid
6020             },
6021             method: 'GET',
6022             success : function(req){
6023                //console.log(data);
6024                 var rdata = false;
6025                 var edata;
6026                 try  {
6027                    rdata = Roo.decode(req.responseText)
6028                 } catch (e) {
6029                     Roo.log("Invalid data from server..");
6030                     Roo.log(edata);
6031                     return;
6032                 }
6033                 if (!rdata || !rdata.success) {
6034                     Roo.log(rdata);
6035                     Roo.MessageBox.alert(Roo.encode(rdata));
6036                     return;
6037                 }
6038                 var data = rdata.data;
6039                 
6040                 if (this.uploadComplete) {
6041                    Roo.MessageBox.hide();
6042                    return;
6043                 }
6044                    
6045                 if (data){
6046                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6047                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6048                     );
6049                 }
6050                 this.uploadProgress.defer(2000,this);
6051             },
6052        
6053             failure: function(data) {
6054                 Roo.log('progress url failed ');
6055                 Roo.log(data);
6056             },
6057             scope : this
6058         });
6059            
6060     },
6061     
6062     
6063     run : function()
6064     {
6065         // run get Values on the form, so it syncs any secondary forms.
6066         this.form.getValues();
6067         
6068         var o = this.options;
6069         var method = this.getMethod();
6070         var isPost = method == 'POST';
6071         if(o.clientValidation === false || this.form.isValid()){
6072             
6073             if (this.form.progressUrl) {
6074                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6075                     (new Date() * 1) + '' + Math.random());
6076                     
6077             } 
6078             
6079             
6080             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6081                 form:this.form.el.dom,
6082                 url:this.getUrl(!isPost),
6083                 method: method,
6084                 params:isPost ? this.getParams() : null,
6085                 isUpload: this.form.fileUpload
6086             }));
6087             
6088             this.uploadProgress();
6089
6090         }else if (o.clientValidation !== false){ // client validation failed
6091             this.failureType = Roo.form.Action.CLIENT_INVALID;
6092             this.form.afterAction(this, false);
6093         }
6094     },
6095
6096     success : function(response)
6097     {
6098         this.uploadComplete= true;
6099         if (this.haveProgress) {
6100             Roo.MessageBox.hide();
6101         }
6102         
6103         
6104         var result = this.processResponse(response);
6105         if(result === true || result.success){
6106             this.form.afterAction(this, true);
6107             return;
6108         }
6109         if(result.errors){
6110             this.form.markInvalid(result.errors);
6111             this.failureType = Roo.form.Action.SERVER_INVALID;
6112         }
6113         this.form.afterAction(this, false);
6114     },
6115     failure : function(response)
6116     {
6117         this.uploadComplete= true;
6118         if (this.haveProgress) {
6119             Roo.MessageBox.hide();
6120         }
6121         
6122         this.response = response;
6123         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6124         this.form.afterAction(this, false);
6125     },
6126     
6127     handleResponse : function(response){
6128         if(this.form.errorReader){
6129             var rs = this.form.errorReader.read(response);
6130             var errors = [];
6131             if(rs.records){
6132                 for(var i = 0, len = rs.records.length; i < len; i++) {
6133                     var r = rs.records[i];
6134                     errors[i] = r.data;
6135                 }
6136             }
6137             if(errors.length < 1){
6138                 errors = null;
6139             }
6140             return {
6141                 success : rs.success,
6142                 errors : errors
6143             };
6144         }
6145         var ret = false;
6146         try {
6147             ret = Roo.decode(response.responseText);
6148         } catch (e) {
6149             ret = {
6150                 success: false,
6151                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6152                 errors : []
6153             };
6154         }
6155         return ret;
6156         
6157     }
6158 });
6159
6160
6161 Roo.form.Action.Load = function(form, options){
6162     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6163     this.reader = this.form.reader;
6164 };
6165
6166 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6167     type : 'load',
6168
6169     run : function(){
6170         
6171         Roo.Ajax.request(Roo.apply(
6172                 this.createCallback(), {
6173                     method:this.getMethod(),
6174                     url:this.getUrl(false),
6175                     params:this.getParams()
6176         }));
6177     },
6178
6179     success : function(response){
6180         
6181         var result = this.processResponse(response);
6182         if(result === true || !result.success || !result.data){
6183             this.failureType = Roo.form.Action.LOAD_FAILURE;
6184             this.form.afterAction(this, false);
6185             return;
6186         }
6187         this.form.clearInvalid();
6188         this.form.setValues(result.data);
6189         this.form.afterAction(this, true);
6190     },
6191
6192     handleResponse : function(response){
6193         if(this.form.reader){
6194             var rs = this.form.reader.read(response);
6195             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6196             return {
6197                 success : rs.success,
6198                 data : data
6199             };
6200         }
6201         return Roo.decode(response.responseText);
6202     }
6203 });
6204
6205 Roo.form.Action.ACTION_TYPES = {
6206     'load' : Roo.form.Action.Load,
6207     'submit' : Roo.form.Action.Submit
6208 };/*
6209  * - LGPL
6210  *
6211  * form
6212  * 
6213  */
6214
6215 /**
6216  * @class Roo.bootstrap.Form
6217  * @extends Roo.bootstrap.Component
6218  * Bootstrap Form class
6219  * @cfg {String} method  GET | POST (default POST)
6220  * @cfg {String} labelAlign top | left (default top)
6221  * @cfg {String} align left  | right - for navbars
6222  * @cfg {Boolean} loadMask load mask when submit (default true)
6223
6224  * 
6225  * @constructor
6226  * Create a new Form
6227  * @param {Object} config The config object
6228  */
6229
6230
6231 Roo.bootstrap.Form = function(config){
6232     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6233     this.addEvents({
6234         /**
6235          * @event clientvalidation
6236          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6237          * @param {Form} this
6238          * @param {Boolean} valid true if the form has passed client-side validation
6239          */
6240         clientvalidation: true,
6241         /**
6242          * @event beforeaction
6243          * Fires before any action is performed. Return false to cancel the action.
6244          * @param {Form} this
6245          * @param {Action} action The action to be performed
6246          */
6247         beforeaction: true,
6248         /**
6249          * @event actionfailed
6250          * Fires when an action fails.
6251          * @param {Form} this
6252          * @param {Action} action The action that failed
6253          */
6254         actionfailed : true,
6255         /**
6256          * @event actioncomplete
6257          * Fires when an action is completed.
6258          * @param {Form} this
6259          * @param {Action} action The action that completed
6260          */
6261         actioncomplete : true
6262     });
6263     
6264 };
6265
6266 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6267       
6268      /**
6269      * @cfg {String} method
6270      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6271      */
6272     method : 'POST',
6273     /**
6274      * @cfg {String} url
6275      * The URL to use for form actions if one isn't supplied in the action options.
6276      */
6277     /**
6278      * @cfg {Boolean} fileUpload
6279      * Set to true if this form is a file upload.
6280      */
6281      
6282     /**
6283      * @cfg {Object} baseParams
6284      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6285      */
6286       
6287     /**
6288      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6289      */
6290     timeout: 30,
6291     /**
6292      * @cfg {Sting} align (left|right) for navbar forms
6293      */
6294     align : 'left',
6295
6296     // private
6297     activeAction : null,
6298  
6299     /**
6300      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6301      * element by passing it or its id or mask the form itself by passing in true.
6302      * @type Mixed
6303      */
6304     waitMsgTarget : false,
6305     
6306     loadMask : true,
6307     
6308     getAutoCreate : function(){
6309         
6310         var cfg = {
6311             tag: 'form',
6312             method : this.method || 'POST',
6313             id : this.id || Roo.id(),
6314             cls : ''
6315         }
6316         if (this.parent().xtype.match(/^Nav/)) {
6317             cfg.cls = 'navbar-form navbar-' + this.align;
6318             
6319         }
6320         
6321         if (this.labelAlign == 'left' ) {
6322             cfg.cls += ' form-horizontal';
6323         }
6324         
6325         
6326         return cfg;
6327     },
6328     initEvents : function()
6329     {
6330         this.el.on('submit', this.onSubmit, this);
6331         // this was added as random key presses on the form where triggering form submit.
6332         this.el.on('keypress', function(e) {
6333             if (e.getCharCode() != 13) {
6334                 return true;
6335             }
6336             // we might need to allow it for textareas.. and some other items.
6337             // check e.getTarget().
6338             
6339             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6340                 return true;
6341             }
6342         
6343             Roo.log("keypress blocked");
6344             
6345             e.preventDefault();
6346             return false;
6347         });
6348         
6349     },
6350     // private
6351     onSubmit : function(e){
6352         e.stopEvent();
6353     },
6354     
6355      /**
6356      * Returns true if client-side validation on the form is successful.
6357      * @return Boolean
6358      */
6359     isValid : function(){
6360         var items = this.getItems();
6361         var valid = true;
6362         items.each(function(f){
6363            if(!f.validate()){
6364                valid = false;
6365                
6366            }
6367         });
6368         return valid;
6369     },
6370     /**
6371      * Returns true if any fields in this form have changed since their original load.
6372      * @return Boolean
6373      */
6374     isDirty : function(){
6375         var dirty = false;
6376         var items = this.getItems();
6377         items.each(function(f){
6378            if(f.isDirty()){
6379                dirty = true;
6380                return false;
6381            }
6382            return true;
6383         });
6384         return dirty;
6385     },
6386      /**
6387      * Performs a predefined action (submit or load) or custom actions you define on this form.
6388      * @param {String} actionName The name of the action type
6389      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6390      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6391      * accept other config options):
6392      * <pre>
6393 Property          Type             Description
6394 ----------------  ---------------  ----------------------------------------------------------------------------------
6395 url               String           The url for the action (defaults to the form's url)
6396 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6397 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6398 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6399                                    validate the form on the client (defaults to false)
6400      * </pre>
6401      * @return {BasicForm} this
6402      */
6403     doAction : function(action, options){
6404         if(typeof action == 'string'){
6405             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6406         }
6407         if(this.fireEvent('beforeaction', this, action) !== false){
6408             this.beforeAction(action);
6409             action.run.defer(100, action);
6410         }
6411         return this;
6412     },
6413     
6414     // private
6415     beforeAction : function(action){
6416         var o = action.options;
6417         
6418         if(this.loadMask){
6419             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6420         }
6421         // not really supported yet.. ??
6422         
6423         //if(this.waitMsgTarget === true){
6424         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6425         //}else if(this.waitMsgTarget){
6426         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6427         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6428         //}else {
6429         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6430        // }
6431          
6432     },
6433
6434     // private
6435     afterAction : function(action, success){
6436         this.activeAction = null;
6437         var o = action.options;
6438         
6439         //if(this.waitMsgTarget === true){
6440             this.el.unmask();
6441         //}else if(this.waitMsgTarget){
6442         //    this.waitMsgTarget.unmask();
6443         //}else{
6444         //    Roo.MessageBox.updateProgress(1);
6445         //    Roo.MessageBox.hide();
6446        // }
6447         // 
6448         if(success){
6449             if(o.reset){
6450                 this.reset();
6451             }
6452             Roo.callback(o.success, o.scope, [this, action]);
6453             this.fireEvent('actioncomplete', this, action);
6454             
6455         }else{
6456             
6457             // failure condition..
6458             // we have a scenario where updates need confirming.
6459             // eg. if a locking scenario exists..
6460             // we look for { errors : { needs_confirm : true }} in the response.
6461             if (
6462                 (typeof(action.result) != 'undefined')  &&
6463                 (typeof(action.result.errors) != 'undefined')  &&
6464                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6465            ){
6466                 var _t = this;
6467                 Roo.log("not supported yet");
6468                  /*
6469                 
6470                 Roo.MessageBox.confirm(
6471                     "Change requires confirmation",
6472                     action.result.errorMsg,
6473                     function(r) {
6474                         if (r != 'yes') {
6475                             return;
6476                         }
6477                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6478                     }
6479                     
6480                 );
6481                 */
6482                 
6483                 
6484                 return;
6485             }
6486             
6487             Roo.callback(o.failure, o.scope, [this, action]);
6488             // show an error message if no failed handler is set..
6489             if (!this.hasListener('actionfailed')) {
6490                 Roo.log("need to add dialog support");
6491                 /*
6492                 Roo.MessageBox.alert("Error",
6493                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6494                         action.result.errorMsg :
6495                         "Saving Failed, please check your entries or try again"
6496                 );
6497                 */
6498             }
6499             
6500             this.fireEvent('actionfailed', this, action);
6501         }
6502         
6503     },
6504     /**
6505      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6506      * @param {String} id The value to search for
6507      * @return Field
6508      */
6509     findField : function(id){
6510         var items = this.getItems();
6511         var field = items.get(id);
6512         if(!field){
6513              items.each(function(f){
6514                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6515                     field = f;
6516                     return false;
6517                 }
6518                 return true;
6519             });
6520         }
6521         return field || null;
6522     },
6523      /**
6524      * Mark fields in this form invalid in bulk.
6525      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6526      * @return {BasicForm} this
6527      */
6528     markInvalid : function(errors){
6529         if(errors instanceof Array){
6530             for(var i = 0, len = errors.length; i < len; i++){
6531                 var fieldError = errors[i];
6532                 var f = this.findField(fieldError.id);
6533                 if(f){
6534                     f.markInvalid(fieldError.msg);
6535                 }
6536             }
6537         }else{
6538             var field, id;
6539             for(id in errors){
6540                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6541                     field.markInvalid(errors[id]);
6542                 }
6543             }
6544         }
6545         //Roo.each(this.childForms || [], function (f) {
6546         //    f.markInvalid(errors);
6547         //});
6548         
6549         return this;
6550     },
6551
6552     /**
6553      * Set values for fields in this form in bulk.
6554      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6555      * @return {BasicForm} this
6556      */
6557     setValues : function(values){
6558         if(values instanceof Array){ // array of objects
6559             for(var i = 0, len = values.length; i < len; i++){
6560                 var v = values[i];
6561                 var f = this.findField(v.id);
6562                 if(f){
6563                     f.setValue(v.value);
6564                     if(this.trackResetOnLoad){
6565                         f.originalValue = f.getValue();
6566                     }
6567                 }
6568             }
6569         }else{ // object hash
6570             var field, id;
6571             for(id in values){
6572                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6573                     
6574                     if (field.setFromData && 
6575                         field.valueField && 
6576                         field.displayField &&
6577                         // combos' with local stores can 
6578                         // be queried via setValue()
6579                         // to set their value..
6580                         (field.store && !field.store.isLocal)
6581                         ) {
6582                         // it's a combo
6583                         var sd = { };
6584                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6585                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6586                         field.setFromData(sd);
6587                         
6588                     } else {
6589                         field.setValue(values[id]);
6590                     }
6591                     
6592                     
6593                     if(this.trackResetOnLoad){
6594                         field.originalValue = field.getValue();
6595                     }
6596                 }
6597             }
6598         }
6599          
6600         //Roo.each(this.childForms || [], function (f) {
6601         //    f.setValues(values);
6602         //});
6603                 
6604         return this;
6605     },
6606
6607     /**
6608      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6609      * they are returned as an array.
6610      * @param {Boolean} asString
6611      * @return {Object}
6612      */
6613     getValues : function(asString){
6614         //if (this.childForms) {
6615             // copy values from the child forms
6616         //    Roo.each(this.childForms, function (f) {
6617         //        this.setValues(f.getValues());
6618         //    }, this);
6619         //}
6620         
6621         
6622         
6623         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6624         if(asString === true){
6625             return fs;
6626         }
6627         return Roo.urlDecode(fs);
6628     },
6629     
6630     /**
6631      * Returns the fields in this form as an object with key/value pairs. 
6632      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6633      * @return {Object}
6634      */
6635     getFieldValues : function(with_hidden)
6636     {
6637         var items = this.getItems();
6638         var ret = {};
6639         items.each(function(f){
6640             if (!f.getName()) {
6641                 return;
6642             }
6643             var v = f.getValue();
6644             if (f.inputType =='radio') {
6645                 if (typeof(ret[f.getName()]) == 'undefined') {
6646                     ret[f.getName()] = ''; // empty..
6647                 }
6648                 
6649                 if (!f.el.dom.checked) {
6650                     return;
6651                     
6652                 }
6653                 v = f.el.dom.value;
6654                 
6655             }
6656             
6657             // not sure if this supported any more..
6658             if ((typeof(v) == 'object') && f.getRawValue) {
6659                 v = f.getRawValue() ; // dates..
6660             }
6661             // combo boxes where name != hiddenName...
6662             if (f.name != f.getName()) {
6663                 ret[f.name] = f.getRawValue();
6664             }
6665             ret[f.getName()] = v;
6666         });
6667         
6668         return ret;
6669     },
6670
6671     /**
6672      * Clears all invalid messages in this form.
6673      * @return {BasicForm} this
6674      */
6675     clearInvalid : function(){
6676         var items = this.getItems();
6677         
6678         items.each(function(f){
6679            f.clearInvalid();
6680         });
6681         
6682         
6683         
6684         return this;
6685     },
6686
6687     /**
6688      * Resets this form.
6689      * @return {BasicForm} this
6690      */
6691     reset : function(){
6692         var items = this.getItems();
6693         items.each(function(f){
6694             f.reset();
6695         });
6696         
6697         Roo.each(this.childForms || [], function (f) {
6698             f.reset();
6699         });
6700        
6701         
6702         return this;
6703     },
6704     getItems : function()
6705     {
6706         var r=new Roo.util.MixedCollection(false, function(o){
6707             return o.id || (o.id = Roo.id());
6708         });
6709         var iter = function(el) {
6710             if (el.inputEl) {
6711                 r.add(el);
6712             }
6713             if (!el.items) {
6714                 return;
6715             }
6716             Roo.each(el.items,function(e) {
6717                 iter(e);
6718             });
6719             
6720             
6721         };
6722         iter(this);
6723         return r;
6724         
6725         
6726         
6727         
6728     }
6729     
6730 });
6731
6732  
6733 /*
6734  * Based on:
6735  * Ext JS Library 1.1.1
6736  * Copyright(c) 2006-2007, Ext JS, LLC.
6737  *
6738  * Originally Released Under LGPL - original licence link has changed is not relivant.
6739  *
6740  * Fork - LGPL
6741  * <script type="text/javascript">
6742  */
6743 /**
6744  * @class Roo.form.VTypes
6745  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6746  * @singleton
6747  */
6748 Roo.form.VTypes = function(){
6749     // closure these in so they are only created once.
6750     var alpha = /^[a-zA-Z_]+$/;
6751     var alphanum = /^[a-zA-Z0-9_]+$/;
6752     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6753     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6754
6755     // All these messages and functions are configurable
6756     return {
6757         /**
6758          * The function used to validate email addresses
6759          * @param {String} value The email address
6760          */
6761         'email' : function(v){
6762             return email.test(v);
6763         },
6764         /**
6765          * The error text to display when the email validation function returns false
6766          * @type String
6767          */
6768         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6769         /**
6770          * The keystroke filter mask to be applied on email input
6771          * @type RegExp
6772          */
6773         'emailMask' : /[a-z0-9_\.\-@]/i,
6774
6775         /**
6776          * The function used to validate URLs
6777          * @param {String} value The URL
6778          */
6779         'url' : function(v){
6780             return url.test(v);
6781         },
6782         /**
6783          * The error text to display when the url validation function returns false
6784          * @type String
6785          */
6786         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6787         
6788         /**
6789          * The function used to validate alpha values
6790          * @param {String} value The value
6791          */
6792         'alpha' : function(v){
6793             return alpha.test(v);
6794         },
6795         /**
6796          * The error text to display when the alpha validation function returns false
6797          * @type String
6798          */
6799         'alphaText' : 'This field should only contain letters and _',
6800         /**
6801          * The keystroke filter mask to be applied on alpha input
6802          * @type RegExp
6803          */
6804         'alphaMask' : /[a-z_]/i,
6805
6806         /**
6807          * The function used to validate alphanumeric values
6808          * @param {String} value The value
6809          */
6810         'alphanum' : function(v){
6811             return alphanum.test(v);
6812         },
6813         /**
6814          * The error text to display when the alphanumeric validation function returns false
6815          * @type String
6816          */
6817         'alphanumText' : 'This field should only contain letters, numbers and _',
6818         /**
6819          * The keystroke filter mask to be applied on alphanumeric input
6820          * @type RegExp
6821          */
6822         'alphanumMask' : /[a-z0-9_]/i
6823     };
6824 }();/*
6825  * - LGPL
6826  *
6827  * Input
6828  * 
6829  */
6830
6831 /**
6832  * @class Roo.bootstrap.Input
6833  * @extends Roo.bootstrap.Component
6834  * Bootstrap Input class
6835  * @cfg {Boolean} disabled is it disabled
6836  * @cfg {String} fieldLabel - the label associated
6837  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6838  * @cfg {String} name name of the input
6839  * @cfg {string} fieldLabel - the label associated
6840  * @cfg {string}  inputType - input / file submit ...
6841  * @cfg {string} placeholder - placeholder to put in text.
6842  * @cfg {string}  before - input group add on before
6843  * @cfg {string} after - input group add on after
6844  * @cfg {string} size - (lg|sm) or leave empty..
6845  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6846  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6847  * @cfg {Number} md colspan out of 12 for computer-sized screens
6848  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6849  * @cfg {string} value default value of the input
6850  * @cfg {Number} labelWidth set the width of label (0-12)
6851  * @cfg {String} labelAlign (top|left)
6852  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6853  * @cfg {String} align (left|center|right) Default left
6854  * 
6855  * 
6856  * @constructor
6857  * Create a new Input
6858  * @param {Object} config The config object
6859  */
6860
6861 Roo.bootstrap.Input = function(config){
6862     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6863    
6864         this.addEvents({
6865             /**
6866              * @event focus
6867              * Fires when this field receives input focus.
6868              * @param {Roo.form.Field} this
6869              */
6870             focus : true,
6871             /**
6872              * @event blur
6873              * Fires when this field loses input focus.
6874              * @param {Roo.form.Field} this
6875              */
6876             blur : true,
6877             /**
6878              * @event specialkey
6879              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6880              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6881              * @param {Roo.form.Field} this
6882              * @param {Roo.EventObject} e The event object
6883              */
6884             specialkey : true,
6885             /**
6886              * @event change
6887              * Fires just before the field blurs if the field value has changed.
6888              * @param {Roo.form.Field} this
6889              * @param {Mixed} newValue The new value
6890              * @param {Mixed} oldValue The original value
6891              */
6892             change : true,
6893             /**
6894              * @event invalid
6895              * Fires after the field has been marked as invalid.
6896              * @param {Roo.form.Field} this
6897              * @param {String} msg The validation message
6898              */
6899             invalid : true,
6900             /**
6901              * @event valid
6902              * Fires after the field has been validated with no errors.
6903              * @param {Roo.form.Field} this
6904              */
6905             valid : true,
6906              /**
6907              * @event keyup
6908              * Fires after the key up
6909              * @param {Roo.form.Field} this
6910              * @param {Roo.EventObject}  e The event Object
6911              */
6912             keyup : true
6913         });
6914 };
6915
6916 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6917      /**
6918      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6919       automatic validation (defaults to "keyup").
6920      */
6921     validationEvent : "keyup",
6922      /**
6923      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6924      */
6925     validateOnBlur : true,
6926     /**
6927      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6928      */
6929     validationDelay : 250,
6930      /**
6931      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6932      */
6933     focusClass : "x-form-focus",  // not needed???
6934     
6935        
6936     /**
6937      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6938      */
6939     invalidClass : "has-error",
6940     
6941     /**
6942      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6943      */
6944     selectOnFocus : false,
6945     
6946      /**
6947      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6948      */
6949     maskRe : null,
6950        /**
6951      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6952      */
6953     vtype : null,
6954     
6955       /**
6956      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6957      */
6958     disableKeyFilter : false,
6959     
6960        /**
6961      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6962      */
6963     disabled : false,
6964      /**
6965      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6966      */
6967     allowBlank : true,
6968     /**
6969      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6970      */
6971     blankText : "This field is required",
6972     
6973      /**
6974      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6975      */
6976     minLength : 0,
6977     /**
6978      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6979      */
6980     maxLength : Number.MAX_VALUE,
6981     /**
6982      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6983      */
6984     minLengthText : "The minimum length for this field is {0}",
6985     /**
6986      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6987      */
6988     maxLengthText : "The maximum length for this field is {0}",
6989   
6990     
6991     /**
6992      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6993      * If available, this function will be called only after the basic validators all return true, and will be passed the
6994      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6995      */
6996     validator : null,
6997     /**
6998      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6999      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7000      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7001      */
7002     regex : null,
7003     /**
7004      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7005      */
7006     regexText : "",
7007     
7008     
7009     
7010     fieldLabel : '',
7011     inputType : 'text',
7012     
7013     name : false,
7014     placeholder: false,
7015     before : false,
7016     after : false,
7017     size : false,
7018     // private
7019     hasFocus : false,
7020     preventMark: false,
7021     isFormField : true,
7022     value : '',
7023     labelWidth : 2,
7024     labelAlign : false,
7025     readOnly : false,
7026     align : false,
7027     formatedValue : false,
7028     
7029     parentLabelAlign : function()
7030     {
7031         var parent = this;
7032         while (parent.parent()) {
7033             parent = parent.parent();
7034             if (typeof(parent.labelAlign) !='undefined') {
7035                 return parent.labelAlign;
7036             }
7037         }
7038         return 'left';
7039         
7040     },
7041     
7042     getAutoCreate : function(){
7043         
7044         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7045         
7046         var id = Roo.id();
7047         
7048         var cfg = {};
7049         
7050         if(this.inputType != 'hidden'){
7051             cfg.cls = 'form-group' //input-group
7052         }
7053         
7054         var input =  {
7055             tag: 'input',
7056             id : id,
7057             type : this.inputType,
7058             value : this.value,
7059             cls : 'form-control',
7060             placeholder : this.placeholder || ''
7061             
7062         };
7063         
7064         if(this.align){
7065             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7066         }
7067         
7068         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7069             input.maxLength = this.maxLength;
7070         }
7071         
7072         if (this.disabled) {
7073             input.disabled=true;
7074         }
7075         
7076         if (this.readOnly) {
7077             input.readonly=true;
7078         }
7079         
7080         if (this.name) {
7081             input.name = this.name;
7082         }
7083         if (this.size) {
7084             input.cls += ' input-' + this.size;
7085         }
7086         var settings=this;
7087         ['xs','sm','md','lg'].map(function(size){
7088             if (settings[size]) {
7089                 cfg.cls += ' col-' + size + '-' + settings[size];
7090             }
7091         });
7092         
7093         var inputblock = input;
7094         
7095         if (this.before || this.after) {
7096             
7097             inputblock = {
7098                 cls : 'input-group',
7099                 cn :  [] 
7100             };
7101             if (this.before && typeof(this.before) == 'string') {
7102                 
7103                 inputblock.cn.push({
7104                     tag :'span',
7105                     cls : 'roo-input-before input-group-addon',
7106                     html : this.before
7107                 });
7108             }
7109             if (this.before && typeof(this.before) == 'object') {
7110                 this.before = Roo.factory(this.before);
7111                 Roo.log(this.before);
7112                 inputblock.cn.push({
7113                     tag :'span',
7114                     cls : 'roo-input-before input-group-' +
7115                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7116                 });
7117             }
7118             
7119             inputblock.cn.push(input);
7120             
7121             if (this.after && typeof(this.after) == 'string') {
7122                 inputblock.cn.push({
7123                     tag :'span',
7124                     cls : 'roo-input-after input-group-addon',
7125                     html : this.after
7126                 });
7127             }
7128             if (this.after && typeof(this.after) == 'object') {
7129                 this.after = Roo.factory(this.after);
7130                 Roo.log(this.after);
7131                 inputblock.cn.push({
7132                     tag :'span',
7133                     cls : 'roo-input-after input-group-' +
7134                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7135                 });
7136             }
7137         };
7138         
7139         if (align ==='left' && this.fieldLabel.length) {
7140                 Roo.log("left and has label");
7141                 cfg.cn = [
7142                     
7143                     {
7144                         tag: 'label',
7145                         'for' :  id,
7146                         cls : 'control-label col-sm-' + this.labelWidth,
7147                         html : this.fieldLabel
7148                         
7149                     },
7150                     {
7151                         cls : "col-sm-" + (12 - this.labelWidth), 
7152                         cn: [
7153                             inputblock
7154                         ]
7155                     }
7156                     
7157                 ];
7158         } else if ( this.fieldLabel.length) {
7159                 Roo.log(" label");
7160                  cfg.cn = [
7161                    
7162                     {
7163                         tag: 'label',
7164                         //cls : 'input-group-addon',
7165                         html : this.fieldLabel
7166                         
7167                     },
7168                     
7169                     inputblock
7170                     
7171                 ];
7172
7173         } else {
7174             
7175                 Roo.log(" no label && no align");
7176                 cfg.cn = [
7177                     
7178                         inputblock
7179                     
7180                 ];
7181                 
7182                 
7183         };
7184         Roo.log('input-parentType: ' + this.parentType);
7185         
7186         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7187            cfg.cls += ' navbar-form';
7188            Roo.log(cfg);
7189         }
7190         
7191         return cfg;
7192         
7193     },
7194     /**
7195      * return the real input element.
7196      */
7197     inputEl: function ()
7198     {
7199         return this.el.select('input.form-control',true).first();
7200     },
7201     setDisabled : function(v)
7202     {
7203         var i  = this.inputEl().dom;
7204         if (!v) {
7205             i.removeAttribute('disabled');
7206             return;
7207             
7208         }
7209         i.setAttribute('disabled','true');
7210     },
7211     initEvents : function()
7212     {
7213         
7214         this.inputEl().on("keydown" , this.fireKey,  this);
7215         this.inputEl().on("focus", this.onFocus,  this);
7216         this.inputEl().on("blur", this.onBlur,  this);
7217         
7218         this.inputEl().relayEvent('keyup', this);
7219
7220         // reference to original value for reset
7221         this.originalValue = this.getValue();
7222         //Roo.form.TextField.superclass.initEvents.call(this);
7223         if(this.validationEvent == 'keyup'){
7224             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7225             this.inputEl().on('keyup', this.filterValidation, this);
7226         }
7227         else if(this.validationEvent !== false){
7228             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7229         }
7230         
7231         if(this.selectOnFocus){
7232             this.on("focus", this.preFocus, this);
7233             
7234         }
7235         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7236             this.inputEl().on("keypress", this.filterKeys, this);
7237         }
7238        /* if(this.grow){
7239             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7240             this.el.on("click", this.autoSize,  this);
7241         }
7242         */
7243         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7244             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7245         }
7246         
7247         if (typeof(this.before) == 'object') {
7248             this.before.render(this.el.select('.roo-input-before',true).first());
7249         }
7250         if (typeof(this.after) == 'object') {
7251             this.after.render(this.el.select('.roo-input-after',true).first());
7252         }
7253         
7254         
7255     },
7256     filterValidation : function(e){
7257         if(!e.isNavKeyPress()){
7258             this.validationTask.delay(this.validationDelay);
7259         }
7260     },
7261      /**
7262      * Validates the field value
7263      * @return {Boolean} True if the value is valid, else false
7264      */
7265     validate : function(){
7266         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7267         if(this.disabled || this.validateValue(this.getRawValue())){
7268             this.clearInvalid();
7269             return true;
7270         }
7271         return false;
7272     },
7273     
7274     
7275     /**
7276      * Validates a value according to the field's validation rules and marks the field as invalid
7277      * if the validation fails
7278      * @param {Mixed} value The value to validate
7279      * @return {Boolean} True if the value is valid, else false
7280      */
7281     validateValue : function(value){
7282         if(value.length < 1)  { // if it's blank
7283              if(this.allowBlank){
7284                 this.clearInvalid();
7285                 return true;
7286              }else{
7287                 this.markInvalid(this.blankText);
7288                 return false;
7289              }
7290         }
7291         if(value.length < this.minLength){
7292             this.markInvalid(String.format(this.minLengthText, this.minLength));
7293             return false;
7294         }
7295         if(value.length > this.maxLength){
7296             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7297             return false;
7298         }
7299         if(this.vtype){
7300             var vt = Roo.form.VTypes;
7301             if(!vt[this.vtype](value, this)){
7302                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7303                 return false;
7304             }
7305         }
7306         if(typeof this.validator == "function"){
7307             var msg = this.validator(value);
7308             if(msg !== true){
7309                 this.markInvalid(msg);
7310                 return false;
7311             }
7312         }
7313         if(this.regex && !this.regex.test(value)){
7314             this.markInvalid(this.regexText);
7315             return false;
7316         }
7317         return true;
7318     },
7319
7320     
7321     
7322      // private
7323     fireKey : function(e){
7324         //Roo.log('field ' + e.getKey());
7325         if(e.isNavKeyPress()){
7326             this.fireEvent("specialkey", this, e);
7327         }
7328     },
7329     focus : function (selectText){
7330         if(this.rendered){
7331             this.inputEl().focus();
7332             if(selectText === true){
7333                 this.inputEl().dom.select();
7334             }
7335         }
7336         return this;
7337     } ,
7338     
7339     onFocus : function(){
7340         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7341            // this.el.addClass(this.focusClass);
7342         }
7343         if(!this.hasFocus){
7344             this.hasFocus = true;
7345             this.startValue = this.getValue();
7346             this.fireEvent("focus", this);
7347         }
7348     },
7349     
7350     beforeBlur : Roo.emptyFn,
7351
7352     
7353     // private
7354     onBlur : function(){
7355         this.beforeBlur();
7356         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7357             //this.el.removeClass(this.focusClass);
7358         }
7359         this.hasFocus = false;
7360         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7361             this.validate();
7362         }
7363         var v = this.getValue();
7364         if(String(v) !== String(this.startValue)){
7365             this.fireEvent('change', this, v, this.startValue);
7366         }
7367         this.fireEvent("blur", this);
7368     },
7369     
7370     /**
7371      * Resets the current field value to the originally loaded value and clears any validation messages
7372      */
7373     reset : function(){
7374         this.setValue(this.originalValue);
7375         this.clearInvalid();
7376     },
7377      /**
7378      * Returns the name of the field
7379      * @return {Mixed} name The name field
7380      */
7381     getName: function(){
7382         return this.name;
7383     },
7384      /**
7385      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7386      * @return {Mixed} value The field value
7387      */
7388     getValue : function(){
7389         
7390         var v = this.inputEl().getValue();
7391         
7392         return v;
7393     },
7394     /**
7395      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7396      * @return {Mixed} value The field value
7397      */
7398     getRawValue : function(){
7399         var v = this.inputEl().getValue();
7400         
7401         return v;
7402     },
7403     
7404     /**
7405      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7406      * @param {Mixed} value The value to set
7407      */
7408     setRawValue : function(v){
7409         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7410     },
7411     
7412     selectText : function(start, end){
7413         var v = this.getRawValue();
7414         if(v.length > 0){
7415             start = start === undefined ? 0 : start;
7416             end = end === undefined ? v.length : end;
7417             var d = this.inputEl().dom;
7418             if(d.setSelectionRange){
7419                 d.setSelectionRange(start, end);
7420             }else if(d.createTextRange){
7421                 var range = d.createTextRange();
7422                 range.moveStart("character", start);
7423                 range.moveEnd("character", v.length-end);
7424                 range.select();
7425             }
7426         }
7427     },
7428     
7429     /**
7430      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7431      * @param {Mixed} value The value to set
7432      */
7433     setValue : function(v){
7434         this.value = v;
7435         if(this.rendered){
7436             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7437             this.validate();
7438         }
7439     },
7440     
7441     /*
7442     processValue : function(value){
7443         if(this.stripCharsRe){
7444             var newValue = value.replace(this.stripCharsRe, '');
7445             if(newValue !== value){
7446                 this.setRawValue(newValue);
7447                 return newValue;
7448             }
7449         }
7450         return value;
7451     },
7452   */
7453     preFocus : function(){
7454         
7455         if(this.selectOnFocus){
7456             this.inputEl().dom.select();
7457         }
7458     },
7459     filterKeys : function(e){
7460         var k = e.getKey();
7461         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7462             return;
7463         }
7464         var c = e.getCharCode(), cc = String.fromCharCode(c);
7465         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7466             return;
7467         }
7468         if(!this.maskRe.test(cc)){
7469             e.stopEvent();
7470         }
7471     },
7472      /**
7473      * Clear any invalid styles/messages for this field
7474      */
7475     clearInvalid : function(){
7476         
7477         if(!this.el || this.preventMark){ // not rendered
7478             return;
7479         }
7480         this.el.removeClass(this.invalidClass);
7481         /*
7482         switch(this.msgTarget){
7483             case 'qtip':
7484                 this.el.dom.qtip = '';
7485                 break;
7486             case 'title':
7487                 this.el.dom.title = '';
7488                 break;
7489             case 'under':
7490                 if(this.errorEl){
7491                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7492                 }
7493                 break;
7494             case 'side':
7495                 if(this.errorIcon){
7496                     this.errorIcon.dom.qtip = '';
7497                     this.errorIcon.hide();
7498                     this.un('resize', this.alignErrorIcon, this);
7499                 }
7500                 break;
7501             default:
7502                 var t = Roo.getDom(this.msgTarget);
7503                 t.innerHTML = '';
7504                 t.style.display = 'none';
7505                 break;
7506         }
7507         */
7508         this.fireEvent('valid', this);
7509     },
7510      /**
7511      * Mark this field as invalid
7512      * @param {String} msg The validation message
7513      */
7514     markInvalid : function(msg){
7515         if(!this.el  || this.preventMark){ // not rendered
7516             return;
7517         }
7518         this.el.addClass(this.invalidClass);
7519         /*
7520         msg = msg || this.invalidText;
7521         switch(this.msgTarget){
7522             case 'qtip':
7523                 this.el.dom.qtip = msg;
7524                 this.el.dom.qclass = 'x-form-invalid-tip';
7525                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7526                     Roo.QuickTips.enable();
7527                 }
7528                 break;
7529             case 'title':
7530                 this.el.dom.title = msg;
7531                 break;
7532             case 'under':
7533                 if(!this.errorEl){
7534                     var elp = this.el.findParent('.x-form-element', 5, true);
7535                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7536                     this.errorEl.setWidth(elp.getWidth(true)-20);
7537                 }
7538                 this.errorEl.update(msg);
7539                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7540                 break;
7541             case 'side':
7542                 if(!this.errorIcon){
7543                     var elp = this.el.findParent('.x-form-element', 5, true);
7544                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7545                 }
7546                 this.alignErrorIcon();
7547                 this.errorIcon.dom.qtip = msg;
7548                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7549                 this.errorIcon.show();
7550                 this.on('resize', this.alignErrorIcon, this);
7551                 break;
7552             default:
7553                 var t = Roo.getDom(this.msgTarget);
7554                 t.innerHTML = msg;
7555                 t.style.display = this.msgDisplay;
7556                 break;
7557         }
7558         */
7559         this.fireEvent('invalid', this, msg);
7560     },
7561     // private
7562     SafariOnKeyDown : function(event)
7563     {
7564         // this is a workaround for a password hang bug on chrome/ webkit.
7565         
7566         var isSelectAll = false;
7567         
7568         if(this.inputEl().dom.selectionEnd > 0){
7569             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7570         }
7571         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7572             event.preventDefault();
7573             this.setValue('');
7574             return;
7575         }
7576         
7577         if(isSelectAll){ // backspace and delete key
7578             
7579             event.preventDefault();
7580             // this is very hacky as keydown always get's upper case.
7581             //
7582             var cc = String.fromCharCode(event.getCharCode());
7583             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7584             
7585         }
7586     },
7587     adjustWidth : function(tag, w){
7588         tag = tag.toLowerCase();
7589         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7590             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7591                 if(tag == 'input'){
7592                     return w + 2;
7593                 }
7594                 if(tag == 'textarea'){
7595                     return w-2;
7596                 }
7597             }else if(Roo.isOpera){
7598                 if(tag == 'input'){
7599                     return w + 2;
7600                 }
7601                 if(tag == 'textarea'){
7602                     return w-2;
7603                 }
7604             }
7605         }
7606         return w;
7607     }
7608     
7609 });
7610
7611  
7612 /*
7613  * - LGPL
7614  *
7615  * Input
7616  * 
7617  */
7618
7619 /**
7620  * @class Roo.bootstrap.TextArea
7621  * @extends Roo.bootstrap.Input
7622  * Bootstrap TextArea class
7623  * @cfg {Number} cols Specifies the visible width of a text area
7624  * @cfg {Number} rows Specifies the visible number of lines in a text area
7625  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7626  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7627  * @cfg {string} html text
7628  * 
7629  * @constructor
7630  * Create a new TextArea
7631  * @param {Object} config The config object
7632  */
7633
7634 Roo.bootstrap.TextArea = function(config){
7635     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7636    
7637 };
7638
7639 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7640      
7641     cols : false,
7642     rows : 5,
7643     readOnly : false,
7644     warp : 'soft',
7645     resize : false,
7646     value: false,
7647     html: false,
7648     
7649     getAutoCreate : function(){
7650         
7651         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7652         
7653         var id = Roo.id();
7654         
7655         var cfg = {};
7656         
7657         var input =  {
7658             tag: 'textarea',
7659             id : id,
7660             warp : this.warp,
7661             rows : this.rows,
7662             value : this.value || '',
7663             html: this.html || '',
7664             cls : 'form-control',
7665             placeholder : this.placeholder || '' 
7666             
7667         };
7668         
7669         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7670             input.maxLength = this.maxLength;
7671         }
7672         
7673         if(this.resize){
7674             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7675         }
7676         
7677         if(this.cols){
7678             input.cols = this.cols;
7679         }
7680         
7681         if (this.readOnly) {
7682             input.readonly = true;
7683         }
7684         
7685         if (this.name) {
7686             input.name = this.name;
7687         }
7688         
7689         if (this.size) {
7690             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7691         }
7692         
7693         var settings=this;
7694         ['xs','sm','md','lg'].map(function(size){
7695             if (settings[size]) {
7696                 cfg.cls += ' col-' + size + '-' + settings[size];
7697             }
7698         });
7699         
7700         var inputblock = input;
7701         
7702         if (this.before || this.after) {
7703             
7704             inputblock = {
7705                 cls : 'input-group',
7706                 cn :  [] 
7707             };
7708             if (this.before) {
7709                 inputblock.cn.push({
7710                     tag :'span',
7711                     cls : 'input-group-addon',
7712                     html : this.before
7713                 });
7714             }
7715             inputblock.cn.push(input);
7716             if (this.after) {
7717                 inputblock.cn.push({
7718                     tag :'span',
7719                     cls : 'input-group-addon',
7720                     html : this.after
7721                 });
7722             }
7723             
7724         }
7725         
7726         if (align ==='left' && this.fieldLabel.length) {
7727                 Roo.log("left and has label");
7728                 cfg.cn = [
7729                     
7730                     {
7731                         tag: 'label',
7732                         'for' :  id,
7733                         cls : 'control-label col-sm-' + this.labelWidth,
7734                         html : this.fieldLabel
7735                         
7736                     },
7737                     {
7738                         cls : "col-sm-" + (12 - this.labelWidth), 
7739                         cn: [
7740                             inputblock
7741                         ]
7742                     }
7743                     
7744                 ];
7745         } else if ( this.fieldLabel.length) {
7746                 Roo.log(" label");
7747                  cfg.cn = [
7748                    
7749                     {
7750                         tag: 'label',
7751                         //cls : 'input-group-addon',
7752                         html : this.fieldLabel
7753                         
7754                     },
7755                     
7756                     inputblock
7757                     
7758                 ];
7759
7760         } else {
7761             
7762                    Roo.log(" no label && no align");
7763                 cfg.cn = [
7764                     
7765                         inputblock
7766                     
7767                 ];
7768                 
7769                 
7770         }
7771         
7772         if (this.disabled) {
7773             input.disabled=true;
7774         }
7775         
7776         return cfg;
7777         
7778     },
7779     /**
7780      * return the real textarea element.
7781      */
7782     inputEl: function ()
7783     {
7784         return this.el.select('textarea.form-control',true).first();
7785     }
7786 });
7787
7788  
7789 /*
7790  * - LGPL
7791  *
7792  * trigger field - base class for combo..
7793  * 
7794  */
7795  
7796 /**
7797  * @class Roo.bootstrap.TriggerField
7798  * @extends Roo.bootstrap.Input
7799  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7800  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7801  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7802  * for which you can provide a custom implementation.  For example:
7803  * <pre><code>
7804 var trigger = new Roo.bootstrap.TriggerField();
7805 trigger.onTriggerClick = myTriggerFn;
7806 trigger.applyTo('my-field');
7807 </code></pre>
7808  *
7809  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7810  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7811  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7812  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7813  * @constructor
7814  * Create a new TriggerField.
7815  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7816  * to the base TextField)
7817  */
7818 Roo.bootstrap.TriggerField = function(config){
7819     this.mimicing = false;
7820     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7821 };
7822
7823 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7824     /**
7825      * @cfg {String} triggerClass A CSS class to apply to the trigger
7826      */
7827      /**
7828      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7829      */
7830     hideTrigger:false,
7831
7832     /** @cfg {Boolean} grow @hide */
7833     /** @cfg {Number} growMin @hide */
7834     /** @cfg {Number} growMax @hide */
7835
7836     /**
7837      * @hide 
7838      * @method
7839      */
7840     autoSize: Roo.emptyFn,
7841     // private
7842     monitorTab : true,
7843     // private
7844     deferHeight : true,
7845
7846     
7847     actionMode : 'wrap',
7848     
7849     
7850     
7851     getAutoCreate : function(){
7852        
7853         var align = this.labelAlign || this.parentLabelAlign();
7854         
7855         var id = Roo.id();
7856         
7857         var cfg = {
7858             cls: 'form-group' //input-group
7859         };
7860         
7861         
7862         var input =  {
7863             tag: 'input',
7864             id : id,
7865             type : this.inputType,
7866             cls : 'form-control',
7867             autocomplete: 'off',
7868             placeholder : this.placeholder || '' 
7869             
7870         };
7871         if (this.name) {
7872             input.name = this.name;
7873         }
7874         if (this.size) {
7875             input.cls += ' input-' + this.size;
7876         }
7877         
7878         if (this.disabled) {
7879             input.disabled=true;
7880         }
7881         
7882         var inputblock = input;
7883         
7884         if (this.before || this.after) {
7885             
7886             inputblock = {
7887                 cls : 'input-group',
7888                 cn :  [] 
7889             };
7890             if (this.before) {
7891                 inputblock.cn.push({
7892                     tag :'span',
7893                     cls : 'input-group-addon',
7894                     html : this.before
7895                 });
7896             }
7897             inputblock.cn.push(input);
7898             if (this.after) {
7899                 inputblock.cn.push({
7900                     tag :'span',
7901                     cls : 'input-group-addon',
7902                     html : this.after
7903                 });
7904             }
7905             
7906         };
7907         
7908         var box = {
7909             tag: 'div',
7910             cn: [
7911                 {
7912                     tag: 'input',
7913                     type : 'hidden',
7914                     cls: 'form-hidden-field'
7915                 },
7916                 inputblock
7917             ]
7918             
7919         };
7920         
7921         if(this.multiple){
7922             Roo.log('multiple');
7923             
7924             box = {
7925                 tag: 'div',
7926                 cn: [
7927                     {
7928                         tag: 'input',
7929                         type : 'hidden',
7930                         cls: 'form-hidden-field'
7931                     },
7932                     {
7933                         tag: 'ul',
7934                         cls: 'select2-choices',
7935                         cn:[
7936                             {
7937                                 tag: 'li',
7938                                 cls: 'select2-search-field',
7939                                 cn: [
7940
7941                                     inputblock
7942                                 ]
7943                             }
7944                         ]
7945                     }
7946                 ]
7947             }
7948         };
7949         
7950         var combobox = {
7951             cls: 'select2-container input-group',
7952             cn: [
7953                 box
7954 //                {
7955 //                    tag: 'ul',
7956 //                    cls: 'typeahead typeahead-long dropdown-menu',
7957 //                    style: 'display:none'
7958 //                }
7959             ]
7960         };
7961         
7962         if(!this.multiple && this.showToggleBtn){
7963             combobox.cn.push({
7964                 tag :'span',
7965                 cls : 'input-group-addon btn dropdown-toggle',
7966                 cn : [
7967                     {
7968                         tag: 'span',
7969                         cls: 'caret'
7970                     },
7971                     {
7972                         tag: 'span',
7973                         cls: 'combobox-clear',
7974                         cn  : [
7975                             {
7976                                 tag : 'i',
7977                                 cls: 'icon-remove'
7978                             }
7979                         ]
7980                     }
7981                 ]
7982
7983             })
7984         }
7985         
7986         if(this.multiple){
7987             combobox.cls += ' select2-container-multi';
7988         }
7989         
7990         if (align ==='left' && this.fieldLabel.length) {
7991             
7992                 Roo.log("left and has label");
7993                 cfg.cn = [
7994                     
7995                     {
7996                         tag: 'label',
7997                         'for' :  id,
7998                         cls : 'control-label col-sm-' + this.labelWidth,
7999                         html : this.fieldLabel
8000                         
8001                     },
8002                     {
8003                         cls : "col-sm-" + (12 - this.labelWidth), 
8004                         cn: [
8005                             combobox
8006                         ]
8007                     }
8008                     
8009                 ];
8010         } else if ( this.fieldLabel.length) {
8011                 Roo.log(" label");
8012                  cfg.cn = [
8013                    
8014                     {
8015                         tag: 'label',
8016                         //cls : 'input-group-addon',
8017                         html : this.fieldLabel
8018                         
8019                     },
8020                     
8021                     combobox
8022                     
8023                 ];
8024
8025         } else {
8026             
8027                 Roo.log(" no label && no align");
8028                 cfg = combobox
8029                      
8030                 
8031         }
8032          
8033         var settings=this;
8034         ['xs','sm','md','lg'].map(function(size){
8035             if (settings[size]) {
8036                 cfg.cls += ' col-' + size + '-' + settings[size];
8037             }
8038         });
8039         
8040         return cfg;
8041         
8042     },
8043     
8044     
8045     
8046     // private
8047     onResize : function(w, h){
8048 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8049 //        if(typeof w == 'number'){
8050 //            var x = w - this.trigger.getWidth();
8051 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8052 //            this.trigger.setStyle('left', x+'px');
8053 //        }
8054     },
8055
8056     // private
8057     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8058
8059     // private
8060     getResizeEl : function(){
8061         return this.inputEl();
8062     },
8063
8064     // private
8065     getPositionEl : function(){
8066         return this.inputEl();
8067     },
8068
8069     // private
8070     alignErrorIcon : function(){
8071         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8072     },
8073
8074     // private
8075     initEvents : function(){
8076         
8077         this.createList();
8078         
8079         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8080         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8081         if(!this.multiple && this.showToggleBtn){
8082             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8083             if(this.hideTrigger){
8084                 this.trigger.setDisplayed(false);
8085             }
8086             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8087         }
8088         
8089         if(this.multiple){
8090             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8091         }
8092         
8093         //this.trigger.addClassOnOver('x-form-trigger-over');
8094         //this.trigger.addClassOnClick('x-form-trigger-click');
8095         
8096         //if(!this.width){
8097         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8098         //}
8099     },
8100     
8101     createList : function()
8102     {
8103         this.list = Roo.get(document.body).createChild({
8104             tag: 'ul',
8105             cls: 'typeahead typeahead-long dropdown-menu',
8106             style: 'display:none'
8107         });
8108         
8109         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8110         
8111     },
8112
8113     // private
8114     initTrigger : function(){
8115        
8116     },
8117
8118     // private
8119     onDestroy : function(){
8120         if(this.trigger){
8121             this.trigger.removeAllListeners();
8122           //  this.trigger.remove();
8123         }
8124         //if(this.wrap){
8125         //    this.wrap.remove();
8126         //}
8127         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8128     },
8129
8130     // private
8131     onFocus : function(){
8132         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8133         /*
8134         if(!this.mimicing){
8135             this.wrap.addClass('x-trigger-wrap-focus');
8136             this.mimicing = true;
8137             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8138             if(this.monitorTab){
8139                 this.el.on("keydown", this.checkTab, this);
8140             }
8141         }
8142         */
8143     },
8144
8145     // private
8146     checkTab : function(e){
8147         if(e.getKey() == e.TAB){
8148             this.triggerBlur();
8149         }
8150     },
8151
8152     // private
8153     onBlur : function(){
8154         // do nothing
8155     },
8156
8157     // private
8158     mimicBlur : function(e, t){
8159         /*
8160         if(!this.wrap.contains(t) && this.validateBlur()){
8161             this.triggerBlur();
8162         }
8163         */
8164     },
8165
8166     // private
8167     triggerBlur : function(){
8168         this.mimicing = false;
8169         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8170         if(this.monitorTab){
8171             this.el.un("keydown", this.checkTab, this);
8172         }
8173         //this.wrap.removeClass('x-trigger-wrap-focus');
8174         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8175     },
8176
8177     // private
8178     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8179     validateBlur : function(e, t){
8180         return true;
8181     },
8182
8183     // private
8184     onDisable : function(){
8185         this.inputEl().dom.disabled = true;
8186         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8187         //if(this.wrap){
8188         //    this.wrap.addClass('x-item-disabled');
8189         //}
8190     },
8191
8192     // private
8193     onEnable : function(){
8194         this.inputEl().dom.disabled = false;
8195         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8196         //if(this.wrap){
8197         //    this.el.removeClass('x-item-disabled');
8198         //}
8199     },
8200
8201     // private
8202     onShow : function(){
8203         var ae = this.getActionEl();
8204         
8205         if(ae){
8206             ae.dom.style.display = '';
8207             ae.dom.style.visibility = 'visible';
8208         }
8209     },
8210
8211     // private
8212     
8213     onHide : function(){
8214         var ae = this.getActionEl();
8215         ae.dom.style.display = 'none';
8216     },
8217
8218     /**
8219      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8220      * by an implementing function.
8221      * @method
8222      * @param {EventObject} e
8223      */
8224     onTriggerClick : Roo.emptyFn
8225 });
8226  /*
8227  * Based on:
8228  * Ext JS Library 1.1.1
8229  * Copyright(c) 2006-2007, Ext JS, LLC.
8230  *
8231  * Originally Released Under LGPL - original licence link has changed is not relivant.
8232  *
8233  * Fork - LGPL
8234  * <script type="text/javascript">
8235  */
8236
8237
8238 /**
8239  * @class Roo.data.SortTypes
8240  * @singleton
8241  * Defines the default sorting (casting?) comparison functions used when sorting data.
8242  */
8243 Roo.data.SortTypes = {
8244     /**
8245      * Default sort that does nothing
8246      * @param {Mixed} s The value being converted
8247      * @return {Mixed} The comparison value
8248      */
8249     none : function(s){
8250         return s;
8251     },
8252     
8253     /**
8254      * The regular expression used to strip tags
8255      * @type {RegExp}
8256      * @property
8257      */
8258     stripTagsRE : /<\/?[^>]+>/gi,
8259     
8260     /**
8261      * Strips all HTML tags to sort on text only
8262      * @param {Mixed} s The value being converted
8263      * @return {String} The comparison value
8264      */
8265     asText : function(s){
8266         return String(s).replace(this.stripTagsRE, "");
8267     },
8268     
8269     /**
8270      * Strips all HTML tags to sort on text only - Case insensitive
8271      * @param {Mixed} s The value being converted
8272      * @return {String} The comparison value
8273      */
8274     asUCText : function(s){
8275         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8276     },
8277     
8278     /**
8279      * Case insensitive string
8280      * @param {Mixed} s The value being converted
8281      * @return {String} The comparison value
8282      */
8283     asUCString : function(s) {
8284         return String(s).toUpperCase();
8285     },
8286     
8287     /**
8288      * Date sorting
8289      * @param {Mixed} s The value being converted
8290      * @return {Number} The comparison value
8291      */
8292     asDate : function(s) {
8293         if(!s){
8294             return 0;
8295         }
8296         if(s instanceof Date){
8297             return s.getTime();
8298         }
8299         return Date.parse(String(s));
8300     },
8301     
8302     /**
8303      * Float sorting
8304      * @param {Mixed} s The value being converted
8305      * @return {Float} The comparison value
8306      */
8307     asFloat : function(s) {
8308         var val = parseFloat(String(s).replace(/,/g, ""));
8309         if(isNaN(val)) val = 0;
8310         return val;
8311     },
8312     
8313     /**
8314      * Integer sorting
8315      * @param {Mixed} s The value being converted
8316      * @return {Number} The comparison value
8317      */
8318     asInt : function(s) {
8319         var val = parseInt(String(s).replace(/,/g, ""));
8320         if(isNaN(val)) val = 0;
8321         return val;
8322     }
8323 };/*
8324  * Based on:
8325  * Ext JS Library 1.1.1
8326  * Copyright(c) 2006-2007, Ext JS, LLC.
8327  *
8328  * Originally Released Under LGPL - original licence link has changed is not relivant.
8329  *
8330  * Fork - LGPL
8331  * <script type="text/javascript">
8332  */
8333
8334 /**
8335 * @class Roo.data.Record
8336  * Instances of this class encapsulate both record <em>definition</em> information, and record
8337  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8338  * to access Records cached in an {@link Roo.data.Store} object.<br>
8339  * <p>
8340  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8341  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8342  * objects.<br>
8343  * <p>
8344  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8345  * @constructor
8346  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8347  * {@link #create}. The parameters are the same.
8348  * @param {Array} data An associative Array of data values keyed by the field name.
8349  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8350  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8351  * not specified an integer id is generated.
8352  */
8353 Roo.data.Record = function(data, id){
8354     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8355     this.data = data;
8356 };
8357
8358 /**
8359  * Generate a constructor for a specific record layout.
8360  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8361  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8362  * Each field definition object may contain the following properties: <ul>
8363  * <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,
8364  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8365  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8366  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8367  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8368  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8369  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8370  * this may be omitted.</p></li>
8371  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8372  * <ul><li>auto (Default, implies no conversion)</li>
8373  * <li>string</li>
8374  * <li>int</li>
8375  * <li>float</li>
8376  * <li>boolean</li>
8377  * <li>date</li></ul></p></li>
8378  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8379  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8380  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8381  * by the Reader into an object that will be stored in the Record. It is passed the
8382  * following parameters:<ul>
8383  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8384  * </ul></p></li>
8385  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8386  * </ul>
8387  * <br>usage:<br><pre><code>
8388 var TopicRecord = Roo.data.Record.create(
8389     {name: 'title', mapping: 'topic_title'},
8390     {name: 'author', mapping: 'username'},
8391     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8392     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8393     {name: 'lastPoster', mapping: 'user2'},
8394     {name: 'excerpt', mapping: 'post_text'}
8395 );
8396
8397 var myNewRecord = new TopicRecord({
8398     title: 'Do my job please',
8399     author: 'noobie',
8400     totalPosts: 1,
8401     lastPost: new Date(),
8402     lastPoster: 'Animal',
8403     excerpt: 'No way dude!'
8404 });
8405 myStore.add(myNewRecord);
8406 </code></pre>
8407  * @method create
8408  * @static
8409  */
8410 Roo.data.Record.create = function(o){
8411     var f = function(){
8412         f.superclass.constructor.apply(this, arguments);
8413     };
8414     Roo.extend(f, Roo.data.Record);
8415     var p = f.prototype;
8416     p.fields = new Roo.util.MixedCollection(false, function(field){
8417         return field.name;
8418     });
8419     for(var i = 0, len = o.length; i < len; i++){
8420         p.fields.add(new Roo.data.Field(o[i]));
8421     }
8422     f.getField = function(name){
8423         return p.fields.get(name);  
8424     };
8425     return f;
8426 };
8427
8428 Roo.data.Record.AUTO_ID = 1000;
8429 Roo.data.Record.EDIT = 'edit';
8430 Roo.data.Record.REJECT = 'reject';
8431 Roo.data.Record.COMMIT = 'commit';
8432
8433 Roo.data.Record.prototype = {
8434     /**
8435      * Readonly flag - true if this record has been modified.
8436      * @type Boolean
8437      */
8438     dirty : false,
8439     editing : false,
8440     error: null,
8441     modified: null,
8442
8443     // private
8444     join : function(store){
8445         this.store = store;
8446     },
8447
8448     /**
8449      * Set the named field to the specified value.
8450      * @param {String} name The name of the field to set.
8451      * @param {Object} value The value to set the field to.
8452      */
8453     set : function(name, value){
8454         if(this.data[name] == value){
8455             return;
8456         }
8457         this.dirty = true;
8458         if(!this.modified){
8459             this.modified = {};
8460         }
8461         if(typeof this.modified[name] == 'undefined'){
8462             this.modified[name] = this.data[name];
8463         }
8464         this.data[name] = value;
8465         if(!this.editing && this.store){
8466             this.store.afterEdit(this);
8467         }       
8468     },
8469
8470     /**
8471      * Get the value of the named field.
8472      * @param {String} name The name of the field to get the value of.
8473      * @return {Object} The value of the field.
8474      */
8475     get : function(name){
8476         return this.data[name]; 
8477     },
8478
8479     // private
8480     beginEdit : function(){
8481         this.editing = true;
8482         this.modified = {}; 
8483     },
8484
8485     // private
8486     cancelEdit : function(){
8487         this.editing = false;
8488         delete this.modified;
8489     },
8490
8491     // private
8492     endEdit : function(){
8493         this.editing = false;
8494         if(this.dirty && this.store){
8495             this.store.afterEdit(this);
8496         }
8497     },
8498
8499     /**
8500      * Usually called by the {@link Roo.data.Store} which owns the Record.
8501      * Rejects all changes made to the Record since either creation, or the last commit operation.
8502      * Modified fields are reverted to their original values.
8503      * <p>
8504      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8505      * of reject operations.
8506      */
8507     reject : function(){
8508         var m = this.modified;
8509         for(var n in m){
8510             if(typeof m[n] != "function"){
8511                 this.data[n] = m[n];
8512             }
8513         }
8514         this.dirty = false;
8515         delete this.modified;
8516         this.editing = false;
8517         if(this.store){
8518             this.store.afterReject(this);
8519         }
8520     },
8521
8522     /**
8523      * Usually called by the {@link Roo.data.Store} which owns the Record.
8524      * Commits all changes made to the Record since either creation, or the last commit operation.
8525      * <p>
8526      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8527      * of commit operations.
8528      */
8529     commit : function(){
8530         this.dirty = false;
8531         delete this.modified;
8532         this.editing = false;
8533         if(this.store){
8534             this.store.afterCommit(this);
8535         }
8536     },
8537
8538     // private
8539     hasError : function(){
8540         return this.error != null;
8541     },
8542
8543     // private
8544     clearError : function(){
8545         this.error = null;
8546     },
8547
8548     /**
8549      * Creates a copy of this record.
8550      * @param {String} id (optional) A new record id if you don't want to use this record's id
8551      * @return {Record}
8552      */
8553     copy : function(newId) {
8554         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8555     }
8556 };/*
8557  * Based on:
8558  * Ext JS Library 1.1.1
8559  * Copyright(c) 2006-2007, Ext JS, LLC.
8560  *
8561  * Originally Released Under LGPL - original licence link has changed is not relivant.
8562  *
8563  * Fork - LGPL
8564  * <script type="text/javascript">
8565  */
8566
8567
8568
8569 /**
8570  * @class Roo.data.Store
8571  * @extends Roo.util.Observable
8572  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8573  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8574  * <p>
8575  * 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
8576  * has no knowledge of the format of the data returned by the Proxy.<br>
8577  * <p>
8578  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8579  * instances from the data object. These records are cached and made available through accessor functions.
8580  * @constructor
8581  * Creates a new Store.
8582  * @param {Object} config A config object containing the objects needed for the Store to access data,
8583  * and read the data into Records.
8584  */
8585 Roo.data.Store = function(config){
8586     this.data = new Roo.util.MixedCollection(false);
8587     this.data.getKey = function(o){
8588         return o.id;
8589     };
8590     this.baseParams = {};
8591     // private
8592     this.paramNames = {
8593         "start" : "start",
8594         "limit" : "limit",
8595         "sort" : "sort",
8596         "dir" : "dir",
8597         "multisort" : "_multisort"
8598     };
8599
8600     if(config && config.data){
8601         this.inlineData = config.data;
8602         delete config.data;
8603     }
8604
8605     Roo.apply(this, config);
8606     
8607     if(this.reader){ // reader passed
8608         this.reader = Roo.factory(this.reader, Roo.data);
8609         this.reader.xmodule = this.xmodule || false;
8610         if(!this.recordType){
8611             this.recordType = this.reader.recordType;
8612         }
8613         if(this.reader.onMetaChange){
8614             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8615         }
8616     }
8617
8618     if(this.recordType){
8619         this.fields = this.recordType.prototype.fields;
8620     }
8621     this.modified = [];
8622
8623     this.addEvents({
8624         /**
8625          * @event datachanged
8626          * Fires when the data cache has changed, and a widget which is using this Store
8627          * as a Record cache should refresh its view.
8628          * @param {Store} this
8629          */
8630         datachanged : true,
8631         /**
8632          * @event metachange
8633          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8634          * @param {Store} this
8635          * @param {Object} meta The JSON metadata
8636          */
8637         metachange : true,
8638         /**
8639          * @event add
8640          * Fires when Records have been added to the Store
8641          * @param {Store} this
8642          * @param {Roo.data.Record[]} records The array of Records added
8643          * @param {Number} index The index at which the record(s) were added
8644          */
8645         add : true,
8646         /**
8647          * @event remove
8648          * Fires when a Record has been removed from the Store
8649          * @param {Store} this
8650          * @param {Roo.data.Record} record The Record that was removed
8651          * @param {Number} index The index at which the record was removed
8652          */
8653         remove : true,
8654         /**
8655          * @event update
8656          * Fires when a Record has been updated
8657          * @param {Store} this
8658          * @param {Roo.data.Record} record The Record that was updated
8659          * @param {String} operation The update operation being performed.  Value may be one of:
8660          * <pre><code>
8661  Roo.data.Record.EDIT
8662  Roo.data.Record.REJECT
8663  Roo.data.Record.COMMIT
8664          * </code></pre>
8665          */
8666         update : true,
8667         /**
8668          * @event clear
8669          * Fires when the data cache has been cleared.
8670          * @param {Store} this
8671          */
8672         clear : true,
8673         /**
8674          * @event beforeload
8675          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8676          * the load action will be canceled.
8677          * @param {Store} this
8678          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8679          */
8680         beforeload : true,
8681         /**
8682          * @event beforeloadadd
8683          * Fires after a new set of Records has been loaded.
8684          * @param {Store} this
8685          * @param {Roo.data.Record[]} records The Records that were loaded
8686          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8687          */
8688         beforeloadadd : true,
8689         /**
8690          * @event load
8691          * Fires after a new set of Records has been loaded, before they are added to the store.
8692          * @param {Store} this
8693          * @param {Roo.data.Record[]} records The Records that were loaded
8694          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8695          * @params {Object} return from reader
8696          */
8697         load : true,
8698         /**
8699          * @event loadexception
8700          * Fires if an exception occurs in the Proxy during loading.
8701          * Called with the signature of the Proxy's "loadexception" event.
8702          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8703          * 
8704          * @param {Proxy} 
8705          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8706          * @param {Object} load options 
8707          * @param {Object} jsonData from your request (normally this contains the Exception)
8708          */
8709         loadexception : true
8710     });
8711     
8712     if(this.proxy){
8713         this.proxy = Roo.factory(this.proxy, Roo.data);
8714         this.proxy.xmodule = this.xmodule || false;
8715         this.relayEvents(this.proxy,  ["loadexception"]);
8716     }
8717     this.sortToggle = {};
8718     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8719
8720     Roo.data.Store.superclass.constructor.call(this);
8721
8722     if(this.inlineData){
8723         this.loadData(this.inlineData);
8724         delete this.inlineData;
8725     }
8726 };
8727
8728 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8729      /**
8730     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8731     * without a remote query - used by combo/forms at present.
8732     */
8733     
8734     /**
8735     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8736     */
8737     /**
8738     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8739     */
8740     /**
8741     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8742     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8743     */
8744     /**
8745     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8746     * on any HTTP request
8747     */
8748     /**
8749     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8750     */
8751     /**
8752     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8753     */
8754     multiSort: false,
8755     /**
8756     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8757     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8758     */
8759     remoteSort : false,
8760
8761     /**
8762     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8763      * loaded or when a record is removed. (defaults to false).
8764     */
8765     pruneModifiedRecords : false,
8766
8767     // private
8768     lastOptions : null,
8769
8770     /**
8771      * Add Records to the Store and fires the add event.
8772      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8773      */
8774     add : function(records){
8775         records = [].concat(records);
8776         for(var i = 0, len = records.length; i < len; i++){
8777             records[i].join(this);
8778         }
8779         var index = this.data.length;
8780         this.data.addAll(records);
8781         this.fireEvent("add", this, records, index);
8782     },
8783
8784     /**
8785      * Remove a Record from the Store and fires the remove event.
8786      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8787      */
8788     remove : function(record){
8789         var index = this.data.indexOf(record);
8790         this.data.removeAt(index);
8791         if(this.pruneModifiedRecords){
8792             this.modified.remove(record);
8793         }
8794         this.fireEvent("remove", this, record, index);
8795     },
8796
8797     /**
8798      * Remove all Records from the Store and fires the clear event.
8799      */
8800     removeAll : function(){
8801         this.data.clear();
8802         if(this.pruneModifiedRecords){
8803             this.modified = [];
8804         }
8805         this.fireEvent("clear", this);
8806     },
8807
8808     /**
8809      * Inserts Records to the Store at the given index and fires the add event.
8810      * @param {Number} index The start index at which to insert the passed Records.
8811      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8812      */
8813     insert : function(index, records){
8814         records = [].concat(records);
8815         for(var i = 0, len = records.length; i < len; i++){
8816             this.data.insert(index, records[i]);
8817             records[i].join(this);
8818         }
8819         this.fireEvent("add", this, records, index);
8820     },
8821
8822     /**
8823      * Get the index within the cache of the passed Record.
8824      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8825      * @return {Number} The index of the passed Record. Returns -1 if not found.
8826      */
8827     indexOf : function(record){
8828         return this.data.indexOf(record);
8829     },
8830
8831     /**
8832      * Get the index within the cache of the Record with the passed id.
8833      * @param {String} id The id of the Record to find.
8834      * @return {Number} The index of the Record. Returns -1 if not found.
8835      */
8836     indexOfId : function(id){
8837         return this.data.indexOfKey(id);
8838     },
8839
8840     /**
8841      * Get the Record with the specified id.
8842      * @param {String} id The id of the Record to find.
8843      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8844      */
8845     getById : function(id){
8846         return this.data.key(id);
8847     },
8848
8849     /**
8850      * Get the Record at the specified index.
8851      * @param {Number} index The index of the Record to find.
8852      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8853      */
8854     getAt : function(index){
8855         return this.data.itemAt(index);
8856     },
8857
8858     /**
8859      * Returns a range of Records between specified indices.
8860      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8861      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8862      * @return {Roo.data.Record[]} An array of Records
8863      */
8864     getRange : function(start, end){
8865         return this.data.getRange(start, end);
8866     },
8867
8868     // private
8869     storeOptions : function(o){
8870         o = Roo.apply({}, o);
8871         delete o.callback;
8872         delete o.scope;
8873         this.lastOptions = o;
8874     },
8875
8876     /**
8877      * Loads the Record cache from the configured Proxy using the configured Reader.
8878      * <p>
8879      * If using remote paging, then the first load call must specify the <em>start</em>
8880      * and <em>limit</em> properties in the options.params property to establish the initial
8881      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8882      * <p>
8883      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8884      * and this call will return before the new data has been loaded. Perform any post-processing
8885      * in a callback function, or in a "load" event handler.</strong>
8886      * <p>
8887      * @param {Object} options An object containing properties which control loading options:<ul>
8888      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8889      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8890      * passed the following arguments:<ul>
8891      * <li>r : Roo.data.Record[]</li>
8892      * <li>options: Options object from the load call</li>
8893      * <li>success: Boolean success indicator</li></ul></li>
8894      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8895      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8896      * </ul>
8897      */
8898     load : function(options){
8899         options = options || {};
8900         if(this.fireEvent("beforeload", this, options) !== false){
8901             this.storeOptions(options);
8902             var p = Roo.apply(options.params || {}, this.baseParams);
8903             // if meta was not loaded from remote source.. try requesting it.
8904             if (!this.reader.metaFromRemote) {
8905                 p._requestMeta = 1;
8906             }
8907             if(this.sortInfo && this.remoteSort){
8908                 var pn = this.paramNames;
8909                 p[pn["sort"]] = this.sortInfo.field;
8910                 p[pn["dir"]] = this.sortInfo.direction;
8911             }
8912             if (this.multiSort) {
8913                 var pn = this.paramNames;
8914                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8915             }
8916             
8917             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8918         }
8919     },
8920
8921     /**
8922      * Reloads the Record cache from the configured Proxy using the configured Reader and
8923      * the options from the last load operation performed.
8924      * @param {Object} options (optional) An object containing properties which may override the options
8925      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8926      * the most recently used options are reused).
8927      */
8928     reload : function(options){
8929         this.load(Roo.applyIf(options||{}, this.lastOptions));
8930     },
8931
8932     // private
8933     // Called as a callback by the Reader during a load operation.
8934     loadRecords : function(o, options, success){
8935         if(!o || success === false){
8936             if(success !== false){
8937                 this.fireEvent("load", this, [], options, o);
8938             }
8939             if(options.callback){
8940                 options.callback.call(options.scope || this, [], options, false);
8941             }
8942             return;
8943         }
8944         // if data returned failure - throw an exception.
8945         if (o.success === false) {
8946             // show a message if no listener is registered.
8947             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8948                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8949             }
8950             // loadmask wil be hooked into this..
8951             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8952             return;
8953         }
8954         var r = o.records, t = o.totalRecords || r.length;
8955         
8956         this.fireEvent("beforeloadadd", this, r, options, o);
8957         
8958         if(!options || options.add !== true){
8959             if(this.pruneModifiedRecords){
8960                 this.modified = [];
8961             }
8962             for(var i = 0, len = r.length; i < len; i++){
8963                 r[i].join(this);
8964             }
8965             if(this.snapshot){
8966                 this.data = this.snapshot;
8967                 delete this.snapshot;
8968             }
8969             this.data.clear();
8970             this.data.addAll(r);
8971             this.totalLength = t;
8972             this.applySort();
8973             this.fireEvent("datachanged", this);
8974         }else{
8975             this.totalLength = Math.max(t, this.data.length+r.length);
8976             this.add(r);
8977         }
8978         this.fireEvent("load", this, r, options, o);
8979         if(options.callback){
8980             options.callback.call(options.scope || this, r, options, true);
8981         }
8982     },
8983
8984
8985     /**
8986      * Loads data from a passed data block. A Reader which understands the format of the data
8987      * must have been configured in the constructor.
8988      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8989      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8990      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8991      */
8992     loadData : function(o, append){
8993         var r = this.reader.readRecords(o);
8994         this.loadRecords(r, {add: append}, true);
8995     },
8996
8997     /**
8998      * Gets the number of cached records.
8999      * <p>
9000      * <em>If using paging, this may not be the total size of the dataset. If the data object
9001      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9002      * the data set size</em>
9003      */
9004     getCount : function(){
9005         return this.data.length || 0;
9006     },
9007
9008     /**
9009      * Gets the total number of records in the dataset as returned by the server.
9010      * <p>
9011      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9012      * the dataset size</em>
9013      */
9014     getTotalCount : function(){
9015         return this.totalLength || 0;
9016     },
9017
9018     /**
9019      * Returns the sort state of the Store as an object with two properties:
9020      * <pre><code>
9021  field {String} The name of the field by which the Records are sorted
9022  direction {String} The sort order, "ASC" or "DESC"
9023      * </code></pre>
9024      */
9025     getSortState : function(){
9026         return this.sortInfo;
9027     },
9028
9029     // private
9030     applySort : function(){
9031         if(this.sortInfo && !this.remoteSort){
9032             var s = this.sortInfo, f = s.field;
9033             var st = this.fields.get(f).sortType;
9034             var fn = function(r1, r2){
9035                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9036                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9037             };
9038             this.data.sort(s.direction, fn);
9039             if(this.snapshot && this.snapshot != this.data){
9040                 this.snapshot.sort(s.direction, fn);
9041             }
9042         }
9043     },
9044
9045     /**
9046      * Sets the default sort column and order to be used by the next load operation.
9047      * @param {String} fieldName The name of the field to sort by.
9048      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9049      */
9050     setDefaultSort : function(field, dir){
9051         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9052     },
9053
9054     /**
9055      * Sort the Records.
9056      * If remote sorting is used, the sort is performed on the server, and the cache is
9057      * reloaded. If local sorting is used, the cache is sorted internally.
9058      * @param {String} fieldName The name of the field to sort by.
9059      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9060      */
9061     sort : function(fieldName, dir){
9062         var f = this.fields.get(fieldName);
9063         if(!dir){
9064             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9065             
9066             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9067                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9068             }else{
9069                 dir = f.sortDir;
9070             }
9071         }
9072         this.sortToggle[f.name] = dir;
9073         this.sortInfo = {field: f.name, direction: dir};
9074         if(!this.remoteSort){
9075             this.applySort();
9076             this.fireEvent("datachanged", this);
9077         }else{
9078             this.load(this.lastOptions);
9079         }
9080     },
9081
9082     /**
9083      * Calls the specified function for each of the Records in the cache.
9084      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9085      * Returning <em>false</em> aborts and exits the iteration.
9086      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9087      */
9088     each : function(fn, scope){
9089         this.data.each(fn, scope);
9090     },
9091
9092     /**
9093      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9094      * (e.g., during paging).
9095      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9096      */
9097     getModifiedRecords : function(){
9098         return this.modified;
9099     },
9100
9101     // private
9102     createFilterFn : function(property, value, anyMatch){
9103         if(!value.exec){ // not a regex
9104             value = String(value);
9105             if(value.length == 0){
9106                 return false;
9107             }
9108             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9109         }
9110         return function(r){
9111             return value.test(r.data[property]);
9112         };
9113     },
9114
9115     /**
9116      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9117      * @param {String} property A field on your records
9118      * @param {Number} start The record index to start at (defaults to 0)
9119      * @param {Number} end The last record index to include (defaults to length - 1)
9120      * @return {Number} The sum
9121      */
9122     sum : function(property, start, end){
9123         var rs = this.data.items, v = 0;
9124         start = start || 0;
9125         end = (end || end === 0) ? end : rs.length-1;
9126
9127         for(var i = start; i <= end; i++){
9128             v += (rs[i].data[property] || 0);
9129         }
9130         return v;
9131     },
9132
9133     /**
9134      * Filter the records by a specified property.
9135      * @param {String} field A field on your records
9136      * @param {String/RegExp} value Either a string that the field
9137      * should start with or a RegExp to test against the field
9138      * @param {Boolean} anyMatch True to match any part not just the beginning
9139      */
9140     filter : function(property, value, anyMatch){
9141         var fn = this.createFilterFn(property, value, anyMatch);
9142         return fn ? this.filterBy(fn) : this.clearFilter();
9143     },
9144
9145     /**
9146      * Filter by a function. The specified function will be called with each
9147      * record in this data source. If the function returns true the record is included,
9148      * otherwise it is filtered.
9149      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9150      * @param {Object} scope (optional) The scope of the function (defaults to this)
9151      */
9152     filterBy : function(fn, scope){
9153         this.snapshot = this.snapshot || this.data;
9154         this.data = this.queryBy(fn, scope||this);
9155         this.fireEvent("datachanged", this);
9156     },
9157
9158     /**
9159      * Query the records by a specified property.
9160      * @param {String} field A field on your records
9161      * @param {String/RegExp} value Either a string that the field
9162      * should start with or a RegExp to test against the field
9163      * @param {Boolean} anyMatch True to match any part not just the beginning
9164      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9165      */
9166     query : function(property, value, anyMatch){
9167         var fn = this.createFilterFn(property, value, anyMatch);
9168         return fn ? this.queryBy(fn) : this.data.clone();
9169     },
9170
9171     /**
9172      * Query by a function. The specified function will be called with each
9173      * record in this data source. If the function returns true the record is included
9174      * in the results.
9175      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9176      * @param {Object} scope (optional) The scope of the function (defaults to this)
9177       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9178      **/
9179     queryBy : function(fn, scope){
9180         var data = this.snapshot || this.data;
9181         return data.filterBy(fn, scope||this);
9182     },
9183
9184     /**
9185      * Collects unique values for a particular dataIndex from this store.
9186      * @param {String} dataIndex The property to collect
9187      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9188      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9189      * @return {Array} An array of the unique values
9190      **/
9191     collect : function(dataIndex, allowNull, bypassFilter){
9192         var d = (bypassFilter === true && this.snapshot) ?
9193                 this.snapshot.items : this.data.items;
9194         var v, sv, r = [], l = {};
9195         for(var i = 0, len = d.length; i < len; i++){
9196             v = d[i].data[dataIndex];
9197             sv = String(v);
9198             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9199                 l[sv] = true;
9200                 r[r.length] = v;
9201             }
9202         }
9203         return r;
9204     },
9205
9206     /**
9207      * Revert to a view of the Record cache with no filtering applied.
9208      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9209      */
9210     clearFilter : function(suppressEvent){
9211         if(this.snapshot && this.snapshot != this.data){
9212             this.data = this.snapshot;
9213             delete this.snapshot;
9214             if(suppressEvent !== true){
9215                 this.fireEvent("datachanged", this);
9216             }
9217         }
9218     },
9219
9220     // private
9221     afterEdit : function(record){
9222         if(this.modified.indexOf(record) == -1){
9223             this.modified.push(record);
9224         }
9225         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9226     },
9227     
9228     // private
9229     afterReject : function(record){
9230         this.modified.remove(record);
9231         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9232     },
9233
9234     // private
9235     afterCommit : function(record){
9236         this.modified.remove(record);
9237         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9238     },
9239
9240     /**
9241      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9242      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9243      */
9244     commitChanges : function(){
9245         var m = this.modified.slice(0);
9246         this.modified = [];
9247         for(var i = 0, len = m.length; i < len; i++){
9248             m[i].commit();
9249         }
9250     },
9251
9252     /**
9253      * Cancel outstanding changes on all changed records.
9254      */
9255     rejectChanges : function(){
9256         var m = this.modified.slice(0);
9257         this.modified = [];
9258         for(var i = 0, len = m.length; i < len; i++){
9259             m[i].reject();
9260         }
9261     },
9262
9263     onMetaChange : function(meta, rtype, o){
9264         this.recordType = rtype;
9265         this.fields = rtype.prototype.fields;
9266         delete this.snapshot;
9267         this.sortInfo = meta.sortInfo || this.sortInfo;
9268         this.modified = [];
9269         this.fireEvent('metachange', this, this.reader.meta);
9270     },
9271     
9272     moveIndex : function(data, type)
9273     {
9274         var index = this.indexOf(data);
9275         
9276         var newIndex = index + type;
9277         
9278         this.remove(data);
9279         
9280         this.insert(newIndex, data);
9281         
9282     }
9283 });/*
9284  * Based on:
9285  * Ext JS Library 1.1.1
9286  * Copyright(c) 2006-2007, Ext JS, LLC.
9287  *
9288  * Originally Released Under LGPL - original licence link has changed is not relivant.
9289  *
9290  * Fork - LGPL
9291  * <script type="text/javascript">
9292  */
9293
9294 /**
9295  * @class Roo.data.SimpleStore
9296  * @extends Roo.data.Store
9297  * Small helper class to make creating Stores from Array data easier.
9298  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9299  * @cfg {Array} fields An array of field definition objects, or field name strings.
9300  * @cfg {Array} data The multi-dimensional array of data
9301  * @constructor
9302  * @param {Object} config
9303  */
9304 Roo.data.SimpleStore = function(config){
9305     Roo.data.SimpleStore.superclass.constructor.call(this, {
9306         isLocal : true,
9307         reader: new Roo.data.ArrayReader({
9308                 id: config.id
9309             },
9310             Roo.data.Record.create(config.fields)
9311         ),
9312         proxy : new Roo.data.MemoryProxy(config.data)
9313     });
9314     this.load();
9315 };
9316 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9317  * Based on:
9318  * Ext JS Library 1.1.1
9319  * Copyright(c) 2006-2007, Ext JS, LLC.
9320  *
9321  * Originally Released Under LGPL - original licence link has changed is not relivant.
9322  *
9323  * Fork - LGPL
9324  * <script type="text/javascript">
9325  */
9326
9327 /**
9328 /**
9329  * @extends Roo.data.Store
9330  * @class Roo.data.JsonStore
9331  * Small helper class to make creating Stores for JSON data easier. <br/>
9332 <pre><code>
9333 var store = new Roo.data.JsonStore({
9334     url: 'get-images.php',
9335     root: 'images',
9336     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9337 });
9338 </code></pre>
9339  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9340  * JsonReader and HttpProxy (unless inline data is provided).</b>
9341  * @cfg {Array} fields An array of field definition objects, or field name strings.
9342  * @constructor
9343  * @param {Object} config
9344  */
9345 Roo.data.JsonStore = function(c){
9346     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9347         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9348         reader: new Roo.data.JsonReader(c, c.fields)
9349     }));
9350 };
9351 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9352  * Based on:
9353  * Ext JS Library 1.1.1
9354  * Copyright(c) 2006-2007, Ext JS, LLC.
9355  *
9356  * Originally Released Under LGPL - original licence link has changed is not relivant.
9357  *
9358  * Fork - LGPL
9359  * <script type="text/javascript">
9360  */
9361
9362  
9363 Roo.data.Field = function(config){
9364     if(typeof config == "string"){
9365         config = {name: config};
9366     }
9367     Roo.apply(this, config);
9368     
9369     if(!this.type){
9370         this.type = "auto";
9371     }
9372     
9373     var st = Roo.data.SortTypes;
9374     // named sortTypes are supported, here we look them up
9375     if(typeof this.sortType == "string"){
9376         this.sortType = st[this.sortType];
9377     }
9378     
9379     // set default sortType for strings and dates
9380     if(!this.sortType){
9381         switch(this.type){
9382             case "string":
9383                 this.sortType = st.asUCString;
9384                 break;
9385             case "date":
9386                 this.sortType = st.asDate;
9387                 break;
9388             default:
9389                 this.sortType = st.none;
9390         }
9391     }
9392
9393     // define once
9394     var stripRe = /[\$,%]/g;
9395
9396     // prebuilt conversion function for this field, instead of
9397     // switching every time we're reading a value
9398     if(!this.convert){
9399         var cv, dateFormat = this.dateFormat;
9400         switch(this.type){
9401             case "":
9402             case "auto":
9403             case undefined:
9404                 cv = function(v){ return v; };
9405                 break;
9406             case "string":
9407                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9408                 break;
9409             case "int":
9410                 cv = function(v){
9411                     return v !== undefined && v !== null && v !== '' ?
9412                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9413                     };
9414                 break;
9415             case "float":
9416                 cv = function(v){
9417                     return v !== undefined && v !== null && v !== '' ?
9418                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9419                     };
9420                 break;
9421             case "bool":
9422             case "boolean":
9423                 cv = function(v){ return v === true || v === "true" || v == 1; };
9424                 break;
9425             case "date":
9426                 cv = function(v){
9427                     if(!v){
9428                         return '';
9429                     }
9430                     if(v instanceof Date){
9431                         return v;
9432                     }
9433                     if(dateFormat){
9434                         if(dateFormat == "timestamp"){
9435                             return new Date(v*1000);
9436                         }
9437                         return Date.parseDate(v, dateFormat);
9438                     }
9439                     var parsed = Date.parse(v);
9440                     return parsed ? new Date(parsed) : null;
9441                 };
9442              break;
9443             
9444         }
9445         this.convert = cv;
9446     }
9447 };
9448
9449 Roo.data.Field.prototype = {
9450     dateFormat: null,
9451     defaultValue: "",
9452     mapping: null,
9453     sortType : null,
9454     sortDir : "ASC"
9455 };/*
9456  * Based on:
9457  * Ext JS Library 1.1.1
9458  * Copyright(c) 2006-2007, Ext JS, LLC.
9459  *
9460  * Originally Released Under LGPL - original licence link has changed is not relivant.
9461  *
9462  * Fork - LGPL
9463  * <script type="text/javascript">
9464  */
9465  
9466 // Base class for reading structured data from a data source.  This class is intended to be
9467 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9468
9469 /**
9470  * @class Roo.data.DataReader
9471  * Base class for reading structured data from a data source.  This class is intended to be
9472  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9473  */
9474
9475 Roo.data.DataReader = function(meta, recordType){
9476     
9477     this.meta = meta;
9478     
9479     this.recordType = recordType instanceof Array ? 
9480         Roo.data.Record.create(recordType) : recordType;
9481 };
9482
9483 Roo.data.DataReader.prototype = {
9484      /**
9485      * Create an empty record
9486      * @param {Object} data (optional) - overlay some values
9487      * @return {Roo.data.Record} record created.
9488      */
9489     newRow :  function(d) {
9490         var da =  {};
9491         this.recordType.prototype.fields.each(function(c) {
9492             switch( c.type) {
9493                 case 'int' : da[c.name] = 0; break;
9494                 case 'date' : da[c.name] = new Date(); break;
9495                 case 'float' : da[c.name] = 0.0; break;
9496                 case 'boolean' : da[c.name] = false; break;
9497                 default : da[c.name] = ""; break;
9498             }
9499             
9500         });
9501         return new this.recordType(Roo.apply(da, d));
9502     }
9503     
9504 };/*
9505  * Based on:
9506  * Ext JS Library 1.1.1
9507  * Copyright(c) 2006-2007, Ext JS, LLC.
9508  *
9509  * Originally Released Under LGPL - original licence link has changed is not relivant.
9510  *
9511  * Fork - LGPL
9512  * <script type="text/javascript">
9513  */
9514
9515 /**
9516  * @class Roo.data.DataProxy
9517  * @extends Roo.data.Observable
9518  * This class is an abstract base class for implementations which provide retrieval of
9519  * unformatted data objects.<br>
9520  * <p>
9521  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9522  * (of the appropriate type which knows how to parse the data object) to provide a block of
9523  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9524  * <p>
9525  * Custom implementations must implement the load method as described in
9526  * {@link Roo.data.HttpProxy#load}.
9527  */
9528 Roo.data.DataProxy = function(){
9529     this.addEvents({
9530         /**
9531          * @event beforeload
9532          * Fires before a network request is made to retrieve a data object.
9533          * @param {Object} This DataProxy object.
9534          * @param {Object} params The params parameter to the load function.
9535          */
9536         beforeload : true,
9537         /**
9538          * @event load
9539          * Fires before the load method's callback is called.
9540          * @param {Object} This DataProxy object.
9541          * @param {Object} o The data object.
9542          * @param {Object} arg The callback argument object passed to the load function.
9543          */
9544         load : true,
9545         /**
9546          * @event loadexception
9547          * Fires if an Exception occurs during data retrieval.
9548          * @param {Object} This DataProxy object.
9549          * @param {Object} o The data object.
9550          * @param {Object} arg The callback argument object passed to the load function.
9551          * @param {Object} e The Exception.
9552          */
9553         loadexception : true
9554     });
9555     Roo.data.DataProxy.superclass.constructor.call(this);
9556 };
9557
9558 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9559
9560     /**
9561      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9562      */
9563 /*
9564  * Based on:
9565  * Ext JS Library 1.1.1
9566  * Copyright(c) 2006-2007, Ext JS, LLC.
9567  *
9568  * Originally Released Under LGPL - original licence link has changed is not relivant.
9569  *
9570  * Fork - LGPL
9571  * <script type="text/javascript">
9572  */
9573 /**
9574  * @class Roo.data.MemoryProxy
9575  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9576  * to the Reader when its load method is called.
9577  * @constructor
9578  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9579  */
9580 Roo.data.MemoryProxy = function(data){
9581     if (data.data) {
9582         data = data.data;
9583     }
9584     Roo.data.MemoryProxy.superclass.constructor.call(this);
9585     this.data = data;
9586 };
9587
9588 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9589     /**
9590      * Load data from the requested source (in this case an in-memory
9591      * data object passed to the constructor), read the data object into
9592      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9593      * process that block using the passed callback.
9594      * @param {Object} params This parameter is not used by the MemoryProxy class.
9595      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9596      * object into a block of Roo.data.Records.
9597      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9598      * The function must be passed <ul>
9599      * <li>The Record block object</li>
9600      * <li>The "arg" argument from the load function</li>
9601      * <li>A boolean success indicator</li>
9602      * </ul>
9603      * @param {Object} scope The scope in which to call the callback
9604      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9605      */
9606     load : function(params, reader, callback, scope, arg){
9607         params = params || {};
9608         var result;
9609         try {
9610             result = reader.readRecords(this.data);
9611         }catch(e){
9612             this.fireEvent("loadexception", this, arg, null, e);
9613             callback.call(scope, null, arg, false);
9614             return;
9615         }
9616         callback.call(scope, result, arg, true);
9617     },
9618     
9619     // private
9620     update : function(params, records){
9621         
9622     }
9623 });/*
9624  * Based on:
9625  * Ext JS Library 1.1.1
9626  * Copyright(c) 2006-2007, Ext JS, LLC.
9627  *
9628  * Originally Released Under LGPL - original licence link has changed is not relivant.
9629  *
9630  * Fork - LGPL
9631  * <script type="text/javascript">
9632  */
9633 /**
9634  * @class Roo.data.HttpProxy
9635  * @extends Roo.data.DataProxy
9636  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9637  * configured to reference a certain URL.<br><br>
9638  * <p>
9639  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9640  * from which the running page was served.<br><br>
9641  * <p>
9642  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9643  * <p>
9644  * Be aware that to enable the browser to parse an XML document, the server must set
9645  * the Content-Type header in the HTTP response to "text/xml".
9646  * @constructor
9647  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9648  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9649  * will be used to make the request.
9650  */
9651 Roo.data.HttpProxy = function(conn){
9652     Roo.data.HttpProxy.superclass.constructor.call(this);
9653     // is conn a conn config or a real conn?
9654     this.conn = conn;
9655     this.useAjax = !conn || !conn.events;
9656   
9657 };
9658
9659 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9660     // thse are take from connection...
9661     
9662     /**
9663      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9664      */
9665     /**
9666      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9667      * extra parameters to each request made by this object. (defaults to undefined)
9668      */
9669     /**
9670      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9671      *  to each request made by this object. (defaults to undefined)
9672      */
9673     /**
9674      * @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)
9675      */
9676     /**
9677      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9678      */
9679      /**
9680      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9681      * @type Boolean
9682      */
9683   
9684
9685     /**
9686      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9687      * @type Boolean
9688      */
9689     /**
9690      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9691      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9692      * a finer-grained basis than the DataProxy events.
9693      */
9694     getConnection : function(){
9695         return this.useAjax ? Roo.Ajax : this.conn;
9696     },
9697
9698     /**
9699      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9700      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9701      * process that block using the passed callback.
9702      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9703      * for the request to the remote server.
9704      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9705      * object into a block of Roo.data.Records.
9706      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9707      * The function must be passed <ul>
9708      * <li>The Record block object</li>
9709      * <li>The "arg" argument from the load function</li>
9710      * <li>A boolean success indicator</li>
9711      * </ul>
9712      * @param {Object} scope The scope in which to call the callback
9713      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9714      */
9715     load : function(params, reader, callback, scope, arg){
9716         if(this.fireEvent("beforeload", this, params) !== false){
9717             var  o = {
9718                 params : params || {},
9719                 request: {
9720                     callback : callback,
9721                     scope : scope,
9722                     arg : arg
9723                 },
9724                 reader: reader,
9725                 callback : this.loadResponse,
9726                 scope: this
9727             };
9728             if(this.useAjax){
9729                 Roo.applyIf(o, this.conn);
9730                 if(this.activeRequest){
9731                     Roo.Ajax.abort(this.activeRequest);
9732                 }
9733                 this.activeRequest = Roo.Ajax.request(o);
9734             }else{
9735                 this.conn.request(o);
9736             }
9737         }else{
9738             callback.call(scope||this, null, arg, false);
9739         }
9740     },
9741
9742     // private
9743     loadResponse : function(o, success, response){
9744         delete this.activeRequest;
9745         if(!success){
9746             this.fireEvent("loadexception", this, o, response);
9747             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9748             return;
9749         }
9750         var result;
9751         try {
9752             result = o.reader.read(response);
9753         }catch(e){
9754             this.fireEvent("loadexception", this, o, response, e);
9755             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9756             return;
9757         }
9758         
9759         this.fireEvent("load", this, o, o.request.arg);
9760         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9761     },
9762
9763     // private
9764     update : function(dataSet){
9765
9766     },
9767
9768     // private
9769     updateResponse : function(dataSet){
9770
9771     }
9772 });/*
9773  * Based on:
9774  * Ext JS Library 1.1.1
9775  * Copyright(c) 2006-2007, Ext JS, LLC.
9776  *
9777  * Originally Released Under LGPL - original licence link has changed is not relivant.
9778  *
9779  * Fork - LGPL
9780  * <script type="text/javascript">
9781  */
9782
9783 /**
9784  * @class Roo.data.ScriptTagProxy
9785  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9786  * other than the originating domain of the running page.<br><br>
9787  * <p>
9788  * <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
9789  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9790  * <p>
9791  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9792  * source code that is used as the source inside a &lt;script> tag.<br><br>
9793  * <p>
9794  * In order for the browser to process the returned data, the server must wrap the data object
9795  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9796  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9797  * depending on whether the callback name was passed:
9798  * <p>
9799  * <pre><code>
9800 boolean scriptTag = false;
9801 String cb = request.getParameter("callback");
9802 if (cb != null) {
9803     scriptTag = true;
9804     response.setContentType("text/javascript");
9805 } else {
9806     response.setContentType("application/x-json");
9807 }
9808 Writer out = response.getWriter();
9809 if (scriptTag) {
9810     out.write(cb + "(");
9811 }
9812 out.print(dataBlock.toJsonString());
9813 if (scriptTag) {
9814     out.write(");");
9815 }
9816 </pre></code>
9817  *
9818  * @constructor
9819  * @param {Object} config A configuration object.
9820  */
9821 Roo.data.ScriptTagProxy = function(config){
9822     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9823     Roo.apply(this, config);
9824     this.head = document.getElementsByTagName("head")[0];
9825 };
9826
9827 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9828
9829 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9830     /**
9831      * @cfg {String} url The URL from which to request the data object.
9832      */
9833     /**
9834      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9835      */
9836     timeout : 30000,
9837     /**
9838      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9839      * the server the name of the callback function set up by the load call to process the returned data object.
9840      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9841      * javascript output which calls this named function passing the data object as its only parameter.
9842      */
9843     callbackParam : "callback",
9844     /**
9845      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9846      * name to the request.
9847      */
9848     nocache : true,
9849
9850     /**
9851      * Load data from the configured URL, read the data object into
9852      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9853      * process that block using the passed callback.
9854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9855      * for the request to the remote server.
9856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9857      * object into a block of Roo.data.Records.
9858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9859      * The function must be passed <ul>
9860      * <li>The Record block object</li>
9861      * <li>The "arg" argument from the load function</li>
9862      * <li>A boolean success indicator</li>
9863      * </ul>
9864      * @param {Object} scope The scope in which to call the callback
9865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9866      */
9867     load : function(params, reader, callback, scope, arg){
9868         if(this.fireEvent("beforeload", this, params) !== false){
9869
9870             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9871
9872             var url = this.url;
9873             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9874             if(this.nocache){
9875                 url += "&_dc=" + (new Date().getTime());
9876             }
9877             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9878             var trans = {
9879                 id : transId,
9880                 cb : "stcCallback"+transId,
9881                 scriptId : "stcScript"+transId,
9882                 params : params,
9883                 arg : arg,
9884                 url : url,
9885                 callback : callback,
9886                 scope : scope,
9887                 reader : reader
9888             };
9889             var conn = this;
9890
9891             window[trans.cb] = function(o){
9892                 conn.handleResponse(o, trans);
9893             };
9894
9895             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9896
9897             if(this.autoAbort !== false){
9898                 this.abort();
9899             }
9900
9901             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9902
9903             var script = document.createElement("script");
9904             script.setAttribute("src", url);
9905             script.setAttribute("type", "text/javascript");
9906             script.setAttribute("id", trans.scriptId);
9907             this.head.appendChild(script);
9908
9909             this.trans = trans;
9910         }else{
9911             callback.call(scope||this, null, arg, false);
9912         }
9913     },
9914
9915     // private
9916     isLoading : function(){
9917         return this.trans ? true : false;
9918     },
9919
9920     /**
9921      * Abort the current server request.
9922      */
9923     abort : function(){
9924         if(this.isLoading()){
9925             this.destroyTrans(this.trans);
9926         }
9927     },
9928
9929     // private
9930     destroyTrans : function(trans, isLoaded){
9931         this.head.removeChild(document.getElementById(trans.scriptId));
9932         clearTimeout(trans.timeoutId);
9933         if(isLoaded){
9934             window[trans.cb] = undefined;
9935             try{
9936                 delete window[trans.cb];
9937             }catch(e){}
9938         }else{
9939             // if hasn't been loaded, wait for load to remove it to prevent script error
9940             window[trans.cb] = function(){
9941                 window[trans.cb] = undefined;
9942                 try{
9943                     delete window[trans.cb];
9944                 }catch(e){}
9945             };
9946         }
9947     },
9948
9949     // private
9950     handleResponse : function(o, trans){
9951         this.trans = false;
9952         this.destroyTrans(trans, true);
9953         var result;
9954         try {
9955             result = trans.reader.readRecords(o);
9956         }catch(e){
9957             this.fireEvent("loadexception", this, o, trans.arg, e);
9958             trans.callback.call(trans.scope||window, null, trans.arg, false);
9959             return;
9960         }
9961         this.fireEvent("load", this, o, trans.arg);
9962         trans.callback.call(trans.scope||window, result, trans.arg, true);
9963     },
9964
9965     // private
9966     handleFailure : function(trans){
9967         this.trans = false;
9968         this.destroyTrans(trans, false);
9969         this.fireEvent("loadexception", this, null, trans.arg);
9970         trans.callback.call(trans.scope||window, null, trans.arg, false);
9971     }
9972 });/*
9973  * Based on:
9974  * Ext JS Library 1.1.1
9975  * Copyright(c) 2006-2007, Ext JS, LLC.
9976  *
9977  * Originally Released Under LGPL - original licence link has changed is not relivant.
9978  *
9979  * Fork - LGPL
9980  * <script type="text/javascript">
9981  */
9982
9983 /**
9984  * @class Roo.data.JsonReader
9985  * @extends Roo.data.DataReader
9986  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9987  * based on mappings in a provided Roo.data.Record constructor.
9988  * 
9989  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9990  * in the reply previously. 
9991  * 
9992  * <p>
9993  * Example code:
9994  * <pre><code>
9995 var RecordDef = Roo.data.Record.create([
9996     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9997     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9998 ]);
9999 var myReader = new Roo.data.JsonReader({
10000     totalProperty: "results",    // The property which contains the total dataset size (optional)
10001     root: "rows",                // The property which contains an Array of row objects
10002     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10003 }, RecordDef);
10004 </code></pre>
10005  * <p>
10006  * This would consume a JSON file like this:
10007  * <pre><code>
10008 { 'results': 2, 'rows': [
10009     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10010     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10011 }
10012 </code></pre>
10013  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10014  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10015  * paged from the remote server.
10016  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10017  * @cfg {String} root name of the property which contains the Array of row objects.
10018  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10019  * @constructor
10020  * Create a new JsonReader
10021  * @param {Object} meta Metadata configuration options
10022  * @param {Object} recordType Either an Array of field definition objects,
10023  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10024  */
10025 Roo.data.JsonReader = function(meta, recordType){
10026     
10027     meta = meta || {};
10028     // set some defaults:
10029     Roo.applyIf(meta, {
10030         totalProperty: 'total',
10031         successProperty : 'success',
10032         root : 'data',
10033         id : 'id'
10034     });
10035     
10036     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10037 };
10038 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10039     
10040     /**
10041      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10042      * Used by Store query builder to append _requestMeta to params.
10043      * 
10044      */
10045     metaFromRemote : false,
10046     /**
10047      * This method is only used by a DataProxy which has retrieved data from a remote server.
10048      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10049      * @return {Object} data A data block which is used by an Roo.data.Store object as
10050      * a cache of Roo.data.Records.
10051      */
10052     read : function(response){
10053         var json = response.responseText;
10054        
10055         var o = /* eval:var:o */ eval("("+json+")");
10056         if(!o) {
10057             throw {message: "JsonReader.read: Json object not found"};
10058         }
10059         
10060         if(o.metaData){
10061             
10062             delete this.ef;
10063             this.metaFromRemote = true;
10064             this.meta = o.metaData;
10065             this.recordType = Roo.data.Record.create(o.metaData.fields);
10066             this.onMetaChange(this.meta, this.recordType, o);
10067         }
10068         return this.readRecords(o);
10069     },
10070
10071     // private function a store will implement
10072     onMetaChange : function(meta, recordType, o){
10073
10074     },
10075
10076     /**
10077          * @ignore
10078          */
10079     simpleAccess: function(obj, subsc) {
10080         return obj[subsc];
10081     },
10082
10083         /**
10084          * @ignore
10085          */
10086     getJsonAccessor: function(){
10087         var re = /[\[\.]/;
10088         return function(expr) {
10089             try {
10090                 return(re.test(expr))
10091                     ? new Function("obj", "return obj." + expr)
10092                     : function(obj){
10093                         return obj[expr];
10094                     };
10095             } catch(e){}
10096             return Roo.emptyFn;
10097         };
10098     }(),
10099
10100     /**
10101      * Create a data block containing Roo.data.Records from an XML document.
10102      * @param {Object} o An object which contains an Array of row objects in the property specified
10103      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10104      * which contains the total size of the dataset.
10105      * @return {Object} data A data block which is used by an Roo.data.Store object as
10106      * a cache of Roo.data.Records.
10107      */
10108     readRecords : function(o){
10109         /**
10110          * After any data loads, the raw JSON data is available for further custom processing.
10111          * @type Object
10112          */
10113         this.o = o;
10114         var s = this.meta, Record = this.recordType,
10115             f = Record.prototype.fields, fi = f.items, fl = f.length;
10116
10117 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10118         if (!this.ef) {
10119             if(s.totalProperty) {
10120                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10121                 }
10122                 if(s.successProperty) {
10123                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10124                 }
10125                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10126                 if (s.id) {
10127                         var g = this.getJsonAccessor(s.id);
10128                         this.getId = function(rec) {
10129                                 var r = g(rec);  
10130                                 return (r === undefined || r === "") ? null : r;
10131                         };
10132                 } else {
10133                         this.getId = function(){return null;};
10134                 }
10135             this.ef = [];
10136             for(var jj = 0; jj < fl; jj++){
10137                 f = fi[jj];
10138                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10139                 this.ef[jj] = this.getJsonAccessor(map);
10140             }
10141         }
10142
10143         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10144         if(s.totalProperty){
10145             var vt = parseInt(this.getTotal(o), 10);
10146             if(!isNaN(vt)){
10147                 totalRecords = vt;
10148             }
10149         }
10150         if(s.successProperty){
10151             var vs = this.getSuccess(o);
10152             if(vs === false || vs === 'false'){
10153                 success = false;
10154             }
10155         }
10156         var records = [];
10157             for(var i = 0; i < c; i++){
10158                     var n = root[i];
10159                 var values = {};
10160                 var id = this.getId(n);
10161                 for(var j = 0; j < fl; j++){
10162                     f = fi[j];
10163                 var v = this.ef[j](n);
10164                 if (!f.convert) {
10165                     Roo.log('missing convert for ' + f.name);
10166                     Roo.log(f);
10167                     continue;
10168                 }
10169                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10170                 }
10171                 var record = new Record(values, id);
10172                 record.json = n;
10173                 records[i] = record;
10174             }
10175             return {
10176             raw : o,
10177                 success : success,
10178                 records : records,
10179                 totalRecords : totalRecords
10180             };
10181     }
10182 });/*
10183  * Based on:
10184  * Ext JS Library 1.1.1
10185  * Copyright(c) 2006-2007, Ext JS, LLC.
10186  *
10187  * Originally Released Under LGPL - original licence link has changed is not relivant.
10188  *
10189  * Fork - LGPL
10190  * <script type="text/javascript">
10191  */
10192
10193 /**
10194  * @class Roo.data.ArrayReader
10195  * @extends Roo.data.DataReader
10196  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10197  * Each element of that Array represents a row of data fields. The
10198  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10199  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10200  * <p>
10201  * Example code:.
10202  * <pre><code>
10203 var RecordDef = Roo.data.Record.create([
10204     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10205     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10206 ]);
10207 var myReader = new Roo.data.ArrayReader({
10208     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10209 }, RecordDef);
10210 </code></pre>
10211  * <p>
10212  * This would consume an Array like this:
10213  * <pre><code>
10214 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10215   </code></pre>
10216  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10217  * @constructor
10218  * Create a new JsonReader
10219  * @param {Object} meta Metadata configuration options.
10220  * @param {Object} recordType Either an Array of field definition objects
10221  * as specified to {@link Roo.data.Record#create},
10222  * or an {@link Roo.data.Record} object
10223  * created using {@link Roo.data.Record#create}.
10224  */
10225 Roo.data.ArrayReader = function(meta, recordType){
10226     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10227 };
10228
10229 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10230     /**
10231      * Create a data block containing Roo.data.Records from an XML document.
10232      * @param {Object} o An Array of row objects which represents the dataset.
10233      * @return {Object} data A data block which is used by an Roo.data.Store object as
10234      * a cache of Roo.data.Records.
10235      */
10236     readRecords : function(o){
10237         var sid = this.meta ? this.meta.id : null;
10238         var recordType = this.recordType, fields = recordType.prototype.fields;
10239         var records = [];
10240         var root = o;
10241             for(var i = 0; i < root.length; i++){
10242                     var n = root[i];
10243                 var values = {};
10244                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10245                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10246                 var f = fields.items[j];
10247                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10248                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10249                 v = f.convert(v);
10250                 values[f.name] = v;
10251             }
10252                 var record = new recordType(values, id);
10253                 record.json = n;
10254                 records[records.length] = record;
10255             }
10256             return {
10257                 records : records,
10258                 totalRecords : records.length
10259             };
10260     }
10261 });/*
10262  * - LGPL
10263  * * 
10264  */
10265
10266 /**
10267  * @class Roo.bootstrap.ComboBox
10268  * @extends Roo.bootstrap.TriggerField
10269  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10270  * @cfg {Boolean} append (true|false) default false
10271  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10272  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10273  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10274  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10275  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10276  * @constructor
10277  * Create a new ComboBox.
10278  * @param {Object} config Configuration options
10279  */
10280 Roo.bootstrap.ComboBox = function(config){
10281     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10282     this.addEvents({
10283         /**
10284          * @event expand
10285          * Fires when the dropdown list is expanded
10286              * @param {Roo.bootstrap.ComboBox} combo This combo box
10287              */
10288         'expand' : true,
10289         /**
10290          * @event collapse
10291          * Fires when the dropdown list is collapsed
10292              * @param {Roo.bootstrap.ComboBox} combo This combo box
10293              */
10294         'collapse' : true,
10295         /**
10296          * @event beforeselect
10297          * Fires before a list item is selected. Return false to cancel the selection.
10298              * @param {Roo.bootstrap.ComboBox} combo This combo box
10299              * @param {Roo.data.Record} record The data record returned from the underlying store
10300              * @param {Number} index The index of the selected item in the dropdown list
10301              */
10302         'beforeselect' : true,
10303         /**
10304          * @event select
10305          * Fires when a list item is selected
10306              * @param {Roo.bootstrap.ComboBox} combo This combo box
10307              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10308              * @param {Number} index The index of the selected item in the dropdown list
10309              */
10310         'select' : true,
10311         /**
10312          * @event beforequery
10313          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10314          * The event object passed has these properties:
10315              * @param {Roo.bootstrap.ComboBox} combo This combo box
10316              * @param {String} query The query
10317              * @param {Boolean} forceAll true to force "all" query
10318              * @param {Boolean} cancel true to cancel the query
10319              * @param {Object} e The query event object
10320              */
10321         'beforequery': true,
10322          /**
10323          * @event add
10324          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10325              * @param {Roo.bootstrap.ComboBox} combo This combo box
10326              */
10327         'add' : true,
10328         /**
10329          * @event edit
10330          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10331              * @param {Roo.bootstrap.ComboBox} combo This combo box
10332              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10333              */
10334         'edit' : true,
10335         /**
10336          * @event remove
10337          * Fires when the remove value from the combobox array
10338              * @param {Roo.bootstrap.ComboBox} combo This combo box
10339              */
10340         'remove' : true
10341         
10342     });
10343     
10344     this.item = [];
10345     this.tickItems = [];
10346     
10347     this.selectedIndex = -1;
10348     if(this.mode == 'local'){
10349         if(config.queryDelay === undefined){
10350             this.queryDelay = 10;
10351         }
10352         if(config.minChars === undefined){
10353             this.minChars = 0;
10354         }
10355     }
10356 };
10357
10358 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10359      
10360     /**
10361      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10362      * rendering into an Roo.Editor, defaults to false)
10363      */
10364     /**
10365      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10366      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10367      */
10368     /**
10369      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10370      */
10371     /**
10372      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10373      * the dropdown list (defaults to undefined, with no header element)
10374      */
10375
10376      /**
10377      * @cfg {String/Roo.Template} tpl The template to use to render the output
10378      */
10379      
10380      /**
10381      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10382      */
10383     listWidth: undefined,
10384     /**
10385      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10386      * mode = 'remote' or 'text' if mode = 'local')
10387      */
10388     displayField: undefined,
10389     /**
10390      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10391      * mode = 'remote' or 'value' if mode = 'local'). 
10392      * Note: use of a valueField requires the user make a selection
10393      * in order for a value to be mapped.
10394      */
10395     valueField: undefined,
10396     
10397     
10398     /**
10399      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10400      * field's data value (defaults to the underlying DOM element's name)
10401      */
10402     hiddenName: undefined,
10403     /**
10404      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10405      */
10406     listClass: '',
10407     /**
10408      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10409      */
10410     selectedClass: 'active',
10411     
10412     /**
10413      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10414      */
10415     shadow:'sides',
10416     /**
10417      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10418      * anchor positions (defaults to 'tl-bl')
10419      */
10420     listAlign: 'tl-bl?',
10421     /**
10422      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10423      */
10424     maxHeight: 300,
10425     /**
10426      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10427      * query specified by the allQuery config option (defaults to 'query')
10428      */
10429     triggerAction: 'query',
10430     /**
10431      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10432      * (defaults to 4, does not apply if editable = false)
10433      */
10434     minChars : 4,
10435     /**
10436      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10437      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10438      */
10439     typeAhead: false,
10440     /**
10441      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10442      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10443      */
10444     queryDelay: 500,
10445     /**
10446      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10447      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10448      */
10449     pageSize: 0,
10450     /**
10451      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10452      * when editable = true (defaults to false)
10453      */
10454     selectOnFocus:false,
10455     /**
10456      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10457      */
10458     queryParam: 'query',
10459     /**
10460      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10461      * when mode = 'remote' (defaults to 'Loading...')
10462      */
10463     loadingText: 'Loading...',
10464     /**
10465      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10466      */
10467     resizable: false,
10468     /**
10469      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10470      */
10471     handleHeight : 8,
10472     /**
10473      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10474      * traditional select (defaults to true)
10475      */
10476     editable: true,
10477     /**
10478      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10479      */
10480     allQuery: '',
10481     /**
10482      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10483      */
10484     mode: 'remote',
10485     /**
10486      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10487      * listWidth has a higher value)
10488      */
10489     minListWidth : 70,
10490     /**
10491      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10492      * allow the user to set arbitrary text into the field (defaults to false)
10493      */
10494     forceSelection:false,
10495     /**
10496      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10497      * if typeAhead = true (defaults to 250)
10498      */
10499     typeAheadDelay : 250,
10500     /**
10501      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10502      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10503      */
10504     valueNotFoundText : undefined,
10505     /**
10506      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10507      */
10508     blockFocus : false,
10509     
10510     /**
10511      * @cfg {Boolean} disableClear Disable showing of clear button.
10512      */
10513     disableClear : false,
10514     /**
10515      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10516      */
10517     alwaysQuery : false,
10518     
10519     /**
10520      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10521      */
10522     multiple : false,
10523     
10524     //private
10525     addicon : false,
10526     editicon: false,
10527     
10528     page: 0,
10529     hasQuery: false,
10530     append: false,
10531     loadNext: false,
10532     autoFocus : true,
10533     tickable : false,
10534     btnPosition : 'right',
10535     triggerList : true,
10536     showToggleBtn : true,
10537     // element that contains real text value.. (when hidden is used..)
10538     
10539     getAutoCreate : function()
10540     {
10541         var cfg = false;
10542         
10543         /*
10544          *  Normal ComboBox
10545          */
10546         if(!this.tickable){
10547             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10548             return cfg;
10549         }
10550         
10551         /*
10552          *  ComboBox with tickable selections
10553          */
10554              
10555         var align = this.labelAlign || this.parentLabelAlign();
10556         
10557         cfg = {
10558             cls : 'form-group roo-combobox-tickable' //input-group
10559         };
10560         
10561         
10562         var buttons = {
10563             tag : 'div',
10564             cls : 'tickable-buttons',
10565             cn : [
10566                 {
10567                     tag : 'button',
10568                     type : 'button',
10569                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10570                     html : 'Edit'
10571                 },
10572                 {
10573                     tag : 'button',
10574                     type : 'button',
10575                     name : 'ok',
10576                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10577                     html : 'Done'
10578                 },
10579                 {
10580                     tag : 'button',
10581                     type : 'button',
10582                     name : 'cancel',
10583                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10584                     html : 'Cancel'
10585                 }
10586             ]
10587         };
10588         
10589         var _this = this;
10590         Roo.each(buttons.cn, function(c){
10591             if (_this.size) {
10592                 c.cls += ' btn-' + _this.size;
10593             }
10594
10595             if (_this.disabled) {
10596                 c.disabled = true;
10597             }
10598         });
10599         
10600         var box = {
10601             tag: 'div',
10602             cn: [
10603                 {
10604                     tag: 'input',
10605                     type : 'hidden',
10606                     cls: 'form-hidden-field'
10607                 },
10608                 {
10609                     tag: 'ul',
10610                     cls: 'select2-choices',
10611                     cn:[
10612                         {
10613                             tag: 'li',
10614                             cls: 'select2-search-field',
10615                             cn: [
10616
10617                                 buttons
10618                             ]
10619                         }
10620                     ]
10621                 }
10622             ]
10623         }
10624         
10625         var combobox = {
10626             cls: 'select2-container input-group select2-container-multi',
10627             cn: [
10628                 box
10629 //                {
10630 //                    tag: 'ul',
10631 //                    cls: 'typeahead typeahead-long dropdown-menu',
10632 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10633 //                }
10634             ]
10635         };
10636         
10637         if (align ==='left' && this.fieldLabel.length) {
10638             
10639                 Roo.log("left and has label");
10640                 cfg.cn = [
10641                     
10642                     {
10643                         tag: 'label',
10644                         'for' :  id,
10645                         cls : 'control-label col-sm-' + this.labelWidth,
10646                         html : this.fieldLabel
10647                         
10648                     },
10649                     {
10650                         cls : "col-sm-" + (12 - this.labelWidth), 
10651                         cn: [
10652                             combobox
10653                         ]
10654                     }
10655                     
10656                 ];
10657         } else if ( this.fieldLabel.length) {
10658                 Roo.log(" label");
10659                  cfg.cn = [
10660                    
10661                     {
10662                         tag: 'label',
10663                         //cls : 'input-group-addon',
10664                         html : this.fieldLabel
10665                         
10666                     },
10667                     
10668                     combobox
10669                     
10670                 ];
10671
10672         } else {
10673             
10674                 Roo.log(" no label && no align");
10675                 cfg = combobox
10676                      
10677                 
10678         }
10679          
10680         var settings=this;
10681         ['xs','sm','md','lg'].map(function(size){
10682             if (settings[size]) {
10683                 cfg.cls += ' col-' + size + '-' + settings[size];
10684             }
10685         });
10686         
10687         return cfg;
10688         
10689     },
10690     
10691     // private
10692     initEvents: function()
10693     {
10694         
10695         if (!this.store) {
10696             throw "can not find store for combo";
10697         }
10698         this.store = Roo.factory(this.store, Roo.data);
10699         
10700         if(this.tickable){
10701             this.initTickableEvents();
10702             return;
10703         }
10704         
10705         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10706         
10707         if(this.hiddenName){
10708             
10709             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10710             
10711             this.hiddenField.dom.value =
10712                 this.hiddenValue !== undefined ? this.hiddenValue :
10713                 this.value !== undefined ? this.value : '';
10714
10715             // prevent input submission
10716             this.el.dom.removeAttribute('name');
10717             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10718              
10719              
10720         }
10721         //if(Roo.isGecko){
10722         //    this.el.dom.setAttribute('autocomplete', 'off');
10723         //}
10724         
10725         var cls = 'x-combo-list';
10726         
10727         //this.list = new Roo.Layer({
10728         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10729         //});
10730         
10731         var _this = this;
10732         
10733         (function(){
10734             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10735             _this.list.setWidth(lw);
10736         }).defer(100);
10737         
10738         this.list.on('mouseover', this.onViewOver, this);
10739         this.list.on('mousemove', this.onViewMove, this);
10740         
10741         this.list.on('scroll', this.onViewScroll, this);
10742         
10743         /*
10744         this.list.swallowEvent('mousewheel');
10745         this.assetHeight = 0;
10746
10747         if(this.title){
10748             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10749             this.assetHeight += this.header.getHeight();
10750         }
10751
10752         this.innerList = this.list.createChild({cls:cls+'-inner'});
10753         this.innerList.on('mouseover', this.onViewOver, this);
10754         this.innerList.on('mousemove', this.onViewMove, this);
10755         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10756         
10757         if(this.allowBlank && !this.pageSize && !this.disableClear){
10758             this.footer = this.list.createChild({cls:cls+'-ft'});
10759             this.pageTb = new Roo.Toolbar(this.footer);
10760            
10761         }
10762         if(this.pageSize){
10763             this.footer = this.list.createChild({cls:cls+'-ft'});
10764             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10765                     {pageSize: this.pageSize});
10766             
10767         }
10768         
10769         if (this.pageTb && this.allowBlank && !this.disableClear) {
10770             var _this = this;
10771             this.pageTb.add(new Roo.Toolbar.Fill(), {
10772                 cls: 'x-btn-icon x-btn-clear',
10773                 text: '&#160;',
10774                 handler: function()
10775                 {
10776                     _this.collapse();
10777                     _this.clearValue();
10778                     _this.onSelect(false, -1);
10779                 }
10780             });
10781         }
10782         if (this.footer) {
10783             this.assetHeight += this.footer.getHeight();
10784         }
10785         */
10786             
10787         if(!this.tpl){
10788             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10789         }
10790
10791         this.view = new Roo.View(this.list, this.tpl, {
10792             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10793         });
10794         //this.view.wrapEl.setDisplayed(false);
10795         this.view.on('click', this.onViewClick, this);
10796         
10797         
10798         
10799         this.store.on('beforeload', this.onBeforeLoad, this);
10800         this.store.on('load', this.onLoad, this);
10801         this.store.on('loadexception', this.onLoadException, this);
10802         /*
10803         if(this.resizable){
10804             this.resizer = new Roo.Resizable(this.list,  {
10805                pinned:true, handles:'se'
10806             });
10807             this.resizer.on('resize', function(r, w, h){
10808                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10809                 this.listWidth = w;
10810                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10811                 this.restrictHeight();
10812             }, this);
10813             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10814         }
10815         */
10816         if(!this.editable){
10817             this.editable = true;
10818             this.setEditable(false);
10819         }
10820         
10821         /*
10822         
10823         if (typeof(this.events.add.listeners) != 'undefined') {
10824             
10825             this.addicon = this.wrap.createChild(
10826                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10827        
10828             this.addicon.on('click', function(e) {
10829                 this.fireEvent('add', this);
10830             }, this);
10831         }
10832         if (typeof(this.events.edit.listeners) != 'undefined') {
10833             
10834             this.editicon = this.wrap.createChild(
10835                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10836             if (this.addicon) {
10837                 this.editicon.setStyle('margin-left', '40px');
10838             }
10839             this.editicon.on('click', function(e) {
10840                 
10841                 // we fire even  if inothing is selected..
10842                 this.fireEvent('edit', this, this.lastData );
10843                 
10844             }, this);
10845         }
10846         */
10847         
10848         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10849             "up" : function(e){
10850                 this.inKeyMode = true;
10851                 this.selectPrev();
10852             },
10853
10854             "down" : function(e){
10855                 if(!this.isExpanded()){
10856                     this.onTriggerClick();
10857                 }else{
10858                     this.inKeyMode = true;
10859                     this.selectNext();
10860                 }
10861             },
10862
10863             "enter" : function(e){
10864 //                this.onViewClick();
10865                 //return true;
10866                 this.collapse();
10867                 
10868                 if(this.fireEvent("specialkey", this, e)){
10869                     this.onViewClick(false);
10870                 }
10871                 
10872                 return true;
10873             },
10874
10875             "esc" : function(e){
10876                 this.collapse();
10877             },
10878
10879             "tab" : function(e){
10880                 this.collapse();
10881                 
10882                 if(this.fireEvent("specialkey", this, e)){
10883                     this.onViewClick(false);
10884                 }
10885                 
10886                 return true;
10887             },
10888
10889             scope : this,
10890
10891             doRelay : function(foo, bar, hname){
10892                 if(hname == 'down' || this.scope.isExpanded()){
10893                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10894                 }
10895                 return true;
10896             },
10897
10898             forceKeyDown: true
10899         });
10900         
10901         
10902         this.queryDelay = Math.max(this.queryDelay || 10,
10903                 this.mode == 'local' ? 10 : 250);
10904         
10905         
10906         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10907         
10908         if(this.typeAhead){
10909             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10910         }
10911         if(this.editable !== false){
10912             this.inputEl().on("keyup", this.onKeyUp, this);
10913         }
10914         if(this.forceSelection){
10915             this.inputEl().on('blur', this.doForce, this);
10916         }
10917         
10918         if(this.multiple){
10919             this.choices = this.el.select('ul.select2-choices', true).first();
10920             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10921         }
10922     },
10923     
10924     initTickableEvents: function()
10925     {   
10926         this.createList();
10927         
10928         if(this.hiddenName){
10929             
10930             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10931             
10932             this.hiddenField.dom.value =
10933                 this.hiddenValue !== undefined ? this.hiddenValue :
10934                 this.value !== undefined ? this.value : '';
10935
10936             // prevent input submission
10937             this.el.dom.removeAttribute('name');
10938             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10939              
10940              
10941         }
10942         
10943 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10944         
10945         this.choices = this.el.select('ul.select2-choices', true).first();
10946         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10947         if(this.triggerList){
10948             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10949         }
10950          
10951         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10952         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10953         
10954         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10955         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10956         
10957         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10958         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10959         
10960         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10961         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10962         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10963         
10964         this.okBtn.hide();
10965         this.cancelBtn.hide();
10966         
10967         var _this = this;
10968         
10969         (function(){
10970             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10971             _this.list.setWidth(lw);
10972         }).defer(100);
10973         
10974         this.list.on('mouseover', this.onViewOver, this);
10975         this.list.on('mousemove', this.onViewMove, this);
10976         
10977         this.list.on('scroll', this.onViewScroll, this);
10978         
10979         if(!this.tpl){
10980             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
10981         }
10982
10983         this.view = new Roo.View(this.list, this.tpl, {
10984             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10985         });
10986         
10987         //this.view.wrapEl.setDisplayed(false);
10988         this.view.on('click', this.onViewClick, this);
10989         
10990         
10991         
10992         this.store.on('beforeload', this.onBeforeLoad, this);
10993         this.store.on('load', this.onLoad, this);
10994         this.store.on('loadexception', this.onLoadException, this);
10995         
10996 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10997 //            "up" : function(e){
10998 //                this.inKeyMode = true;
10999 //                this.selectPrev();
11000 //            },
11001 //
11002 //            "down" : function(e){
11003 //                if(!this.isExpanded()){
11004 //                    this.onTriggerClick();
11005 //                }else{
11006 //                    this.inKeyMode = true;
11007 //                    this.selectNext();
11008 //                }
11009 //            },
11010 //
11011 //            "enter" : function(e){
11012 ////                this.onViewClick();
11013 //                //return true;
11014 //                this.collapse();
11015 //                
11016 //                if(this.fireEvent("specialkey", this, e)){
11017 //                    this.onViewClick(false);
11018 //                }
11019 //                
11020 //                return true;
11021 //            },
11022 //
11023 //            "esc" : function(e){
11024 //                this.collapse();
11025 //            },
11026 //
11027 //            "tab" : function(e){
11028 //                this.collapse();
11029 //                
11030 //                if(this.fireEvent("specialkey", this, e)){
11031 //                    this.onViewClick(false);
11032 //                }
11033 //                
11034 //                return true;
11035 //            },
11036 //
11037 //            scope : this,
11038 //
11039 //            doRelay : function(foo, bar, hname){
11040 //                if(hname == 'down' || this.scope.isExpanded()){
11041 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11042 //                }
11043 //                return true;
11044 //            },
11045 //
11046 //            forceKeyDown: true
11047 //        });
11048         
11049         
11050         this.queryDelay = Math.max(this.queryDelay || 10,
11051                 this.mode == 'local' ? 10 : 250);
11052         
11053         
11054         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11055         
11056         if(this.typeAhead){
11057             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11058         }
11059     },
11060
11061     onDestroy : function(){
11062         if(this.view){
11063             this.view.setStore(null);
11064             this.view.el.removeAllListeners();
11065             this.view.el.remove();
11066             this.view.purgeListeners();
11067         }
11068         if(this.list){
11069             this.list.dom.innerHTML  = '';
11070         }
11071         
11072         if(this.store){
11073             this.store.un('beforeload', this.onBeforeLoad, this);
11074             this.store.un('load', this.onLoad, this);
11075             this.store.un('loadexception', this.onLoadException, this);
11076         }
11077         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11078     },
11079
11080     // private
11081     fireKey : function(e){
11082         if(e.isNavKeyPress() && !this.list.isVisible()){
11083             this.fireEvent("specialkey", this, e);
11084         }
11085     },
11086
11087     // private
11088     onResize: function(w, h){
11089 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11090 //        
11091 //        if(typeof w != 'number'){
11092 //            // we do not handle it!?!?
11093 //            return;
11094 //        }
11095 //        var tw = this.trigger.getWidth();
11096 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11097 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11098 //        var x = w - tw;
11099 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11100 //            
11101 //        //this.trigger.setStyle('left', x+'px');
11102 //        
11103 //        if(this.list && this.listWidth === undefined){
11104 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11105 //            this.list.setWidth(lw);
11106 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11107 //        }
11108         
11109     
11110         
11111     },
11112
11113     /**
11114      * Allow or prevent the user from directly editing the field text.  If false is passed,
11115      * the user will only be able to select from the items defined in the dropdown list.  This method
11116      * is the runtime equivalent of setting the 'editable' config option at config time.
11117      * @param {Boolean} value True to allow the user to directly edit the field text
11118      */
11119     setEditable : function(value){
11120         if(value == this.editable){
11121             return;
11122         }
11123         this.editable = value;
11124         if(!value){
11125             this.inputEl().dom.setAttribute('readOnly', true);
11126             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11127             this.inputEl().addClass('x-combo-noedit');
11128         }else{
11129             this.inputEl().dom.setAttribute('readOnly', false);
11130             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11131             this.inputEl().removeClass('x-combo-noedit');
11132         }
11133     },
11134
11135     // private
11136     
11137     onBeforeLoad : function(combo,opts){
11138         if(!this.hasFocus){
11139             return;
11140         }
11141          if (!opts.add) {
11142             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11143          }
11144         this.restrictHeight();
11145         this.selectedIndex = -1;
11146     },
11147
11148     // private
11149     onLoad : function(){
11150         
11151         this.hasQuery = false;
11152         
11153         if(!this.hasFocus){
11154             return;
11155         }
11156         
11157         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11158             this.loading.hide();
11159         }
11160         
11161         if(this.store.getCount() > 0){
11162             this.expand();
11163 //            this.restrictHeight();
11164             if(this.lastQuery == this.allQuery){
11165                 if(this.editable && !this.tickable){
11166                     this.inputEl().dom.select();
11167                 }
11168                 
11169                 if(
11170                     !this.selectByValue(this.value, true) &&
11171                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11172                     this.store.lastOptions.add != true)
11173                 ){
11174                     this.select(0, true);
11175                 }
11176             }else{
11177                 if(this.autoFocus){
11178                     this.selectNext();
11179                 }
11180                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11181                     this.taTask.delay(this.typeAheadDelay);
11182                 }
11183             }
11184         }else{
11185             this.onEmptyResults();
11186         }
11187         
11188         //this.el.focus();
11189     },
11190     // private
11191     onLoadException : function()
11192     {
11193         this.hasQuery = false;
11194         
11195         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11196             this.loading.hide();
11197         }
11198         
11199         this.collapse();
11200         Roo.log(this.store.reader.jsonData);
11201         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11202             // fixme
11203             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11204         }
11205         
11206         
11207     },
11208     // private
11209     onTypeAhead : function(){
11210         if(this.store.getCount() > 0){
11211             var r = this.store.getAt(0);
11212             var newValue = r.data[this.displayField];
11213             var len = newValue.length;
11214             var selStart = this.getRawValue().length;
11215             
11216             if(selStart != len){
11217                 this.setRawValue(newValue);
11218                 this.selectText(selStart, newValue.length);
11219             }
11220         }
11221     },
11222
11223     // private
11224     onSelect : function(record, index){
11225         
11226         if(this.fireEvent('beforeselect', this, record, index) !== false){
11227         
11228             this.setFromData(index > -1 ? record.data : false);
11229             
11230             this.collapse();
11231             this.fireEvent('select', this, record, index);
11232         }
11233     },
11234
11235     /**
11236      * Returns the currently selected field value or empty string if no value is set.
11237      * @return {String} value The selected value
11238      */
11239     getValue : function(){
11240         
11241         if(this.multiple){
11242             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11243         }
11244         
11245         if(this.valueField){
11246             return typeof this.value != 'undefined' ? this.value : '';
11247         }else{
11248             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11249         }
11250     },
11251
11252     /**
11253      * Clears any text/value currently set in the field
11254      */
11255     clearValue : function(){
11256         if(this.hiddenField){
11257             this.hiddenField.dom.value = '';
11258         }
11259         this.value = '';
11260         this.setRawValue('');
11261         this.lastSelectionText = '';
11262         
11263     },
11264
11265     /**
11266      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11267      * will be displayed in the field.  If the value does not match the data value of an existing item,
11268      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11269      * Otherwise the field will be blank (although the value will still be set).
11270      * @param {String} value The value to match
11271      */
11272     setValue : function(v){
11273         if(this.multiple){
11274             this.syncValue();
11275             return;
11276         }
11277         
11278         var text = v;
11279         if(this.valueField){
11280             var r = this.findRecord(this.valueField, v);
11281             if(r){
11282                 text = r.data[this.displayField];
11283             }else if(this.valueNotFoundText !== undefined){
11284                 text = this.valueNotFoundText;
11285             }
11286         }
11287         this.lastSelectionText = text;
11288         if(this.hiddenField){
11289             this.hiddenField.dom.value = v;
11290         }
11291         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11292         this.value = v;
11293     },
11294     /**
11295      * @property {Object} the last set data for the element
11296      */
11297     
11298     lastData : false,
11299     /**
11300      * Sets the value of the field based on a object which is related to the record format for the store.
11301      * @param {Object} value the value to set as. or false on reset?
11302      */
11303     setFromData : function(o){
11304         
11305         if(this.multiple){
11306             this.addItem(o);
11307             return;
11308         }
11309             
11310         var dv = ''; // display value
11311         var vv = ''; // value value..
11312         this.lastData = o;
11313         if (this.displayField) {
11314             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11315         } else {
11316             // this is an error condition!!!
11317             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11318         }
11319         
11320         if(this.valueField){
11321             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11322         }
11323         
11324         if(this.hiddenField){
11325             this.hiddenField.dom.value = vv;
11326             
11327             this.lastSelectionText = dv;
11328             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11329             this.value = vv;
11330             return;
11331         }
11332         // no hidden field.. - we store the value in 'value', but still display
11333         // display field!!!!
11334         this.lastSelectionText = dv;
11335         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11336         this.value = vv;
11337         
11338         
11339     },
11340     // private
11341     reset : function(){
11342         // overridden so that last data is reset..
11343         this.setValue(this.originalValue);
11344         this.clearInvalid();
11345         this.lastData = false;
11346         if (this.view) {
11347             this.view.clearSelections();
11348         }
11349     },
11350     // private
11351     findRecord : function(prop, value){
11352         var record;
11353         if(this.store.getCount() > 0){
11354             this.store.each(function(r){
11355                 if(r.data[prop] == value){
11356                     record = r;
11357                     return false;
11358                 }
11359                 return true;
11360             });
11361         }
11362         return record;
11363     },
11364     
11365     getName: function()
11366     {
11367         // returns hidden if it's set..
11368         if (!this.rendered) {return ''};
11369         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11370         
11371     },
11372     // private
11373     onViewMove : function(e, t){
11374         this.inKeyMode = false;
11375     },
11376
11377     // private
11378     onViewOver : function(e, t){
11379         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11380             return;
11381         }
11382         var item = this.view.findItemFromChild(t);
11383         
11384         if(item){
11385             var index = this.view.indexOf(item);
11386             this.select(index, false);
11387         }
11388     },
11389
11390     // private
11391     onViewClick : function(view, doFocus, el, e)
11392     {
11393         var index = this.view.getSelectedIndexes()[0];
11394         
11395         var r = this.store.getAt(index);
11396         
11397         if(this.tickable){
11398             
11399             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11400                 return;
11401             }
11402             
11403             var rm = false;
11404             var _this = this;
11405             
11406             Roo.each(this.tickItems, function(v,k){
11407                 
11408                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11409                     _this.tickItems.splice(k, 1);
11410                     rm = true;
11411                     return;
11412                 }
11413             })
11414             
11415             if(rm){
11416                 return;
11417             }
11418             
11419             this.tickItems.push(r.data);
11420             return;
11421         }
11422         
11423         if(r){
11424             this.onSelect(r, index);
11425         }
11426         if(doFocus !== false && !this.blockFocus){
11427             this.inputEl().focus();
11428         }
11429     },
11430
11431     // private
11432     restrictHeight : function(){
11433         //this.innerList.dom.style.height = '';
11434         //var inner = this.innerList.dom;
11435         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11436         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11437         //this.list.beginUpdate();
11438         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11439         this.list.alignTo(this.inputEl(), this.listAlign);
11440         this.list.alignTo(this.inputEl(), this.listAlign);
11441         //this.list.endUpdate();
11442     },
11443
11444     // private
11445     onEmptyResults : function(){
11446         this.collapse();
11447     },
11448
11449     /**
11450      * Returns true if the dropdown list is expanded, else false.
11451      */
11452     isExpanded : function(){
11453         return this.list.isVisible();
11454     },
11455
11456     /**
11457      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11458      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11459      * @param {String} value The data value of the item to select
11460      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11461      * selected item if it is not currently in view (defaults to true)
11462      * @return {Boolean} True if the value matched an item in the list, else false
11463      */
11464     selectByValue : function(v, scrollIntoView){
11465         if(v !== undefined && v !== null){
11466             var r = this.findRecord(this.valueField || this.displayField, v);
11467             if(r){
11468                 this.select(this.store.indexOf(r), scrollIntoView);
11469                 return true;
11470             }
11471         }
11472         return false;
11473     },
11474
11475     /**
11476      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11477      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11478      * @param {Number} index The zero-based index of the list item to select
11479      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11480      * selected item if it is not currently in view (defaults to true)
11481      */
11482     select : function(index, scrollIntoView){
11483         this.selectedIndex = index;
11484         this.view.select(index);
11485         if(scrollIntoView !== false){
11486             var el = this.view.getNode(index);
11487             if(el && !this.multiple && !this.tickable){
11488                 this.list.scrollChildIntoView(el, false);
11489             }
11490         }
11491     },
11492
11493     // private
11494     selectNext : function(){
11495         var ct = this.store.getCount();
11496         if(ct > 0){
11497             if(this.selectedIndex == -1){
11498                 this.select(0);
11499             }else if(this.selectedIndex < ct-1){
11500                 this.select(this.selectedIndex+1);
11501             }
11502         }
11503     },
11504
11505     // private
11506     selectPrev : function(){
11507         var ct = this.store.getCount();
11508         if(ct > 0){
11509             if(this.selectedIndex == -1){
11510                 this.select(0);
11511             }else if(this.selectedIndex != 0){
11512                 this.select(this.selectedIndex-1);
11513             }
11514         }
11515     },
11516
11517     // private
11518     onKeyUp : function(e){
11519         if(this.editable !== false && !e.isSpecialKey()){
11520             this.lastKey = e.getKey();
11521             this.dqTask.delay(this.queryDelay);
11522         }
11523     },
11524
11525     // private
11526     validateBlur : function(){
11527         return !this.list || !this.list.isVisible();   
11528     },
11529
11530     // private
11531     initQuery : function(){
11532         this.doQuery(this.getRawValue());
11533     },
11534
11535     // private
11536     doForce : function(){
11537         if(this.inputEl().dom.value.length > 0){
11538             this.inputEl().dom.value =
11539                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11540              
11541         }
11542     },
11543
11544     /**
11545      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11546      * query allowing the query action to be canceled if needed.
11547      * @param {String} query The SQL query to execute
11548      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11549      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11550      * saved in the current store (defaults to false)
11551      */
11552     doQuery : function(q, forceAll){
11553         
11554         if(q === undefined || q === null){
11555             q = '';
11556         }
11557         var qe = {
11558             query: q,
11559             forceAll: forceAll,
11560             combo: this,
11561             cancel:false
11562         };
11563         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11564             return false;
11565         }
11566         q = qe.query;
11567         
11568         forceAll = qe.forceAll;
11569         if(forceAll === true || (q.length >= this.minChars)){
11570             
11571             this.hasQuery = true;
11572             
11573             if(this.lastQuery != q || this.alwaysQuery){
11574                 this.lastQuery = q;
11575                 if(this.mode == 'local'){
11576                     this.selectedIndex = -1;
11577                     if(forceAll){
11578                         this.store.clearFilter();
11579                     }else{
11580                         this.store.filter(this.displayField, q);
11581                     }
11582                     this.onLoad();
11583                 }else{
11584                     this.store.baseParams[this.queryParam] = q;
11585                     
11586                     var options = {params : this.getParams(q)};
11587                     
11588                     if(this.loadNext){
11589                         options.add = true;
11590                         options.params.start = this.page * this.pageSize;
11591                     }
11592                     
11593                     this.store.load(options);
11594                     /*
11595                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11596                      *  we should expand the list on onLoad
11597                      *  so command out it
11598                      */
11599 //                    this.expand();
11600                 }
11601             }else{
11602                 this.selectedIndex = -1;
11603                 this.onLoad();   
11604             }
11605         }
11606         
11607         this.loadNext = false;
11608     },
11609
11610     // private
11611     getParams : function(q){
11612         var p = {};
11613         //p[this.queryParam] = q;
11614         
11615         if(this.pageSize){
11616             p.start = 0;
11617             p.limit = this.pageSize;
11618         }
11619         return p;
11620     },
11621
11622     /**
11623      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11624      */
11625     collapse : function(){
11626         if(!this.isExpanded()){
11627             return;
11628         }
11629         
11630         this.list.hide();
11631         
11632         if(this.tickable){
11633             this.okBtn.hide();
11634             this.cancelBtn.hide();
11635             this.trigger.show();
11636         }
11637         
11638         Roo.get(document).un('mousedown', this.collapseIf, this);
11639         Roo.get(document).un('mousewheel', this.collapseIf, this);
11640         if (!this.editable) {
11641             Roo.get(document).un('keydown', this.listKeyPress, this);
11642         }
11643         this.fireEvent('collapse', this);
11644     },
11645
11646     // private
11647     collapseIf : function(e){
11648         var in_combo  = e.within(this.el);
11649         var in_list =  e.within(this.list);
11650         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11651         
11652         if (in_combo || in_list || is_list) {
11653             //e.stopPropagation();
11654             return;
11655         }
11656         
11657         if(this.tickable){
11658             this.onTickableFooterButtonClick(e, false, false);
11659         }
11660
11661         this.collapse();
11662         
11663     },
11664
11665     /**
11666      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11667      */
11668     expand : function(){
11669        
11670         if(this.isExpanded() || !this.hasFocus){
11671             return;
11672         }
11673         
11674         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11675         this.list.setWidth(lw);
11676         
11677         
11678          Roo.log('expand');
11679         
11680         this.list.show();
11681         
11682         this.restrictHeight();
11683         
11684         if(this.tickable){
11685             
11686             this.tickItems = Roo.apply([], this.item);
11687             
11688             this.okBtn.show();
11689             this.cancelBtn.show();
11690             this.trigger.hide();
11691             
11692         }
11693         
11694         Roo.get(document).on('mousedown', this.collapseIf, this);
11695         Roo.get(document).on('mousewheel', this.collapseIf, this);
11696         if (!this.editable) {
11697             Roo.get(document).on('keydown', this.listKeyPress, this);
11698         }
11699         
11700         this.fireEvent('expand', this);
11701     },
11702
11703     // private
11704     // Implements the default empty TriggerField.onTriggerClick function
11705     onTriggerClick : function(e)
11706     {
11707         Roo.log('trigger click');
11708         
11709         if(this.disabled || !this.triggerList){
11710             return;
11711         }
11712         
11713         this.page = 0;
11714         this.loadNext = false;
11715         
11716         if(this.isExpanded()){
11717             this.collapse();
11718             if (!this.blockFocus) {
11719                 this.inputEl().focus();
11720             }
11721             
11722         }else {
11723             this.hasFocus = true;
11724             if(this.triggerAction == 'all') {
11725                 this.doQuery(this.allQuery, true);
11726             } else {
11727                 this.doQuery(this.getRawValue());
11728             }
11729             if (!this.blockFocus) {
11730                 this.inputEl().focus();
11731             }
11732         }
11733     },
11734     
11735     onTickableTriggerClick : function(e)
11736     {
11737         if(this.disabled){
11738             return;
11739         }
11740         
11741         this.page = 0;
11742         this.loadNext = false;
11743         this.hasFocus = true;
11744         
11745         if(this.triggerAction == 'all') {
11746             this.doQuery(this.allQuery, true);
11747         } else {
11748             this.doQuery(this.getRawValue());
11749         }
11750     },
11751     
11752     onSearchFieldClick : function(e)
11753     {
11754         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11755             return;
11756         }
11757         
11758         this.page = 0;
11759         this.loadNext = false;
11760         this.hasFocus = true;
11761         
11762         if(this.triggerAction == 'all') {
11763             this.doQuery(this.allQuery, true);
11764         } else {
11765             this.doQuery(this.getRawValue());
11766         }
11767     },
11768     
11769     listKeyPress : function(e)
11770     {
11771         //Roo.log('listkeypress');
11772         // scroll to first matching element based on key pres..
11773         if (e.isSpecialKey()) {
11774             return false;
11775         }
11776         var k = String.fromCharCode(e.getKey()).toUpperCase();
11777         //Roo.log(k);
11778         var match  = false;
11779         var csel = this.view.getSelectedNodes();
11780         var cselitem = false;
11781         if (csel.length) {
11782             var ix = this.view.indexOf(csel[0]);
11783             cselitem  = this.store.getAt(ix);
11784             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11785                 cselitem = false;
11786             }
11787             
11788         }
11789         
11790         this.store.each(function(v) { 
11791             if (cselitem) {
11792                 // start at existing selection.
11793                 if (cselitem.id == v.id) {
11794                     cselitem = false;
11795                 }
11796                 return true;
11797             }
11798                 
11799             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11800                 match = this.store.indexOf(v);
11801                 return false;
11802             }
11803             return true;
11804         }, this);
11805         
11806         if (match === false) {
11807             return true; // no more action?
11808         }
11809         // scroll to?
11810         this.view.select(match);
11811         var sn = Roo.get(this.view.getSelectedNodes()[0])
11812         //sn.scrollIntoView(sn.dom.parentNode, false);
11813     },
11814     
11815     onViewScroll : function(e, t){
11816         
11817         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11818             return;
11819         }
11820         
11821         this.hasQuery = true;
11822         
11823         this.loading = this.list.select('.loading', true).first();
11824         
11825         if(this.loading === null){
11826             this.list.createChild({
11827                 tag: 'div',
11828                 cls: 'loading select2-more-results select2-active',
11829                 html: 'Loading more results...'
11830             })
11831             
11832             this.loading = this.list.select('.loading', true).first();
11833             
11834             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11835             
11836             this.loading.hide();
11837         }
11838         
11839         this.loading.show();
11840         
11841         var _combo = this;
11842         
11843         this.page++;
11844         this.loadNext = true;
11845         
11846         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11847         
11848         return;
11849     },
11850     
11851     addItem : function(o)
11852     {   
11853         var dv = ''; // display value
11854         
11855         if (this.displayField) {
11856             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11857         } else {
11858             // this is an error condition!!!
11859             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11860         }
11861         
11862         if(!dv.length){
11863             return;
11864         }
11865         
11866         var choice = this.choices.createChild({
11867             tag: 'li',
11868             cls: 'select2-search-choice',
11869             cn: [
11870                 {
11871                     tag: 'div',
11872                     html: dv
11873                 },
11874                 {
11875                     tag: 'a',
11876                     href: '#',
11877                     cls: 'select2-search-choice-close',
11878                     tabindex: '-1'
11879                 }
11880             ]
11881             
11882         }, this.searchField);
11883         
11884         var close = choice.select('a.select2-search-choice-close', true).first()
11885         
11886         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11887         
11888         this.item.push(o);
11889         
11890         this.lastData = o;
11891         
11892         this.syncValue();
11893         
11894         this.inputEl().dom.value = '';
11895         
11896     },
11897     
11898     onRemoveItem : function(e, _self, o)
11899     {
11900         e.preventDefault();
11901         var index = this.item.indexOf(o.data) * 1;
11902         
11903         if( index < 0){
11904             Roo.log('not this item?!');
11905             return;
11906         }
11907         
11908         this.item.splice(index, 1);
11909         o.item.remove();
11910         
11911         this.syncValue();
11912         
11913         this.fireEvent('remove', this, e);
11914         
11915     },
11916     
11917     syncValue : function()
11918     {
11919         if(!this.item.length){
11920             this.clearValue();
11921             return;
11922         }
11923             
11924         var value = [];
11925         var _this = this;
11926         Roo.each(this.item, function(i){
11927             if(_this.valueField){
11928                 value.push(i[_this.valueField]);
11929                 return;
11930             }
11931
11932             value.push(i);
11933         });
11934
11935         this.value = value.join(',');
11936
11937         if(this.hiddenField){
11938             this.hiddenField.dom.value = this.value;
11939         }
11940     },
11941     
11942     clearItem : function()
11943     {
11944         if(!this.multiple){
11945             return;
11946         }
11947         
11948         this.item = [];
11949         
11950         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11951            c.remove();
11952         });
11953         
11954         this.syncValue();
11955     },
11956     
11957     inputEl: function ()
11958     {
11959         if(this.tickable){
11960             return this.searchField;
11961         }
11962         return this.el.select('input.form-control',true).first();
11963     },
11964     
11965     
11966     onTickableFooterButtonClick : function(e, btn, el)
11967     {
11968         e.preventDefault();
11969         
11970         if(btn && btn.name == 'cancel'){
11971             this.tickItems = Roo.apply([], this.item);
11972             this.collapse();
11973             return;
11974         }
11975         
11976         this.clearItem();
11977         
11978         var _this = this;
11979         
11980         Roo.each(this.tickItems, function(o){
11981             _this.addItem(o);
11982         });
11983         
11984         this.collapse();
11985         
11986     }
11987     
11988     
11989
11990     /** 
11991     * @cfg {Boolean} grow 
11992     * @hide 
11993     */
11994     /** 
11995     * @cfg {Number} growMin 
11996     * @hide 
11997     */
11998     /** 
11999     * @cfg {Number} growMax 
12000     * @hide 
12001     */
12002     /**
12003      * @hide
12004      * @method autoSize
12005      */
12006 });
12007 /*
12008  * Based on:
12009  * Ext JS Library 1.1.1
12010  * Copyright(c) 2006-2007, Ext JS, LLC.
12011  *
12012  * Originally Released Under LGPL - original licence link has changed is not relivant.
12013  *
12014  * Fork - LGPL
12015  * <script type="text/javascript">
12016  */
12017
12018 /**
12019  * @class Roo.View
12020  * @extends Roo.util.Observable
12021  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12022  * This class also supports single and multi selection modes. <br>
12023  * Create a data model bound view:
12024  <pre><code>
12025  var store = new Roo.data.Store(...);
12026
12027  var view = new Roo.View({
12028     el : "my-element",
12029     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12030  
12031     singleSelect: true,
12032     selectedClass: "ydataview-selected",
12033     store: store
12034  });
12035
12036  // listen for node click?
12037  view.on("click", function(vw, index, node, e){
12038  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12039  });
12040
12041  // load XML data
12042  dataModel.load("foobar.xml");
12043  </code></pre>
12044  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12045  * <br><br>
12046  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12047  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12048  * 
12049  * Note: old style constructor is still suported (container, template, config)
12050  * 
12051  * @constructor
12052  * Create a new View
12053  * @param {Object} config The config object
12054  * 
12055  */
12056 Roo.View = function(config, depreciated_tpl, depreciated_config){
12057     
12058     this.parent = false;
12059     
12060     if (typeof(depreciated_tpl) == 'undefined') {
12061         // new way.. - universal constructor.
12062         Roo.apply(this, config);
12063         this.el  = Roo.get(this.el);
12064     } else {
12065         // old format..
12066         this.el  = Roo.get(config);
12067         this.tpl = depreciated_tpl;
12068         Roo.apply(this, depreciated_config);
12069     }
12070     this.wrapEl  = this.el.wrap().wrap();
12071     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12072     
12073     
12074     if(typeof(this.tpl) == "string"){
12075         this.tpl = new Roo.Template(this.tpl);
12076     } else {
12077         // support xtype ctors..
12078         this.tpl = new Roo.factory(this.tpl, Roo);
12079     }
12080     
12081     
12082     this.tpl.compile();
12083     
12084     /** @private */
12085     this.addEvents({
12086         /**
12087          * @event beforeclick
12088          * Fires before a click is processed. Returns false to cancel the default action.
12089          * @param {Roo.View} this
12090          * @param {Number} index The index of the target node
12091          * @param {HTMLElement} node The target node
12092          * @param {Roo.EventObject} e The raw event object
12093          */
12094             "beforeclick" : true,
12095         /**
12096          * @event click
12097          * Fires when a template node is clicked.
12098          * @param {Roo.View} this
12099          * @param {Number} index The index of the target node
12100          * @param {HTMLElement} node The target node
12101          * @param {Roo.EventObject} e The raw event object
12102          */
12103             "click" : true,
12104         /**
12105          * @event dblclick
12106          * Fires when a template node is double clicked.
12107          * @param {Roo.View} this
12108          * @param {Number} index The index of the target node
12109          * @param {HTMLElement} node The target node
12110          * @param {Roo.EventObject} e The raw event object
12111          */
12112             "dblclick" : true,
12113         /**
12114          * @event contextmenu
12115          * Fires when a template node is right clicked.
12116          * @param {Roo.View} this
12117          * @param {Number} index The index of the target node
12118          * @param {HTMLElement} node The target node
12119          * @param {Roo.EventObject} e The raw event object
12120          */
12121             "contextmenu" : true,
12122         /**
12123          * @event selectionchange
12124          * Fires when the selected nodes change.
12125          * @param {Roo.View} this
12126          * @param {Array} selections Array of the selected nodes
12127          */
12128             "selectionchange" : true,
12129     
12130         /**
12131          * @event beforeselect
12132          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12133          * @param {Roo.View} this
12134          * @param {HTMLElement} node The node to be selected
12135          * @param {Array} selections Array of currently selected nodes
12136          */
12137             "beforeselect" : true,
12138         /**
12139          * @event preparedata
12140          * Fires on every row to render, to allow you to change the data.
12141          * @param {Roo.View} this
12142          * @param {Object} data to be rendered (change this)
12143          */
12144           "preparedata" : true
12145           
12146           
12147         });
12148
12149
12150
12151     this.el.on({
12152         "click": this.onClick,
12153         "dblclick": this.onDblClick,
12154         "contextmenu": this.onContextMenu,
12155         scope:this
12156     });
12157
12158     this.selections = [];
12159     this.nodes = [];
12160     this.cmp = new Roo.CompositeElementLite([]);
12161     if(this.store){
12162         this.store = Roo.factory(this.store, Roo.data);
12163         this.setStore(this.store, true);
12164     }
12165     
12166     if ( this.footer && this.footer.xtype) {
12167            
12168          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12169         
12170         this.footer.dataSource = this.store
12171         this.footer.container = fctr;
12172         this.footer = Roo.factory(this.footer, Roo);
12173         fctr.insertFirst(this.el);
12174         
12175         // this is a bit insane - as the paging toolbar seems to detach the el..
12176 //        dom.parentNode.parentNode.parentNode
12177          // they get detached?
12178     }
12179     
12180     
12181     Roo.View.superclass.constructor.call(this);
12182     
12183     
12184 };
12185
12186 Roo.extend(Roo.View, Roo.util.Observable, {
12187     
12188      /**
12189      * @cfg {Roo.data.Store} store Data store to load data from.
12190      */
12191     store : false,
12192     
12193     /**
12194      * @cfg {String|Roo.Element} el The container element.
12195      */
12196     el : '',
12197     
12198     /**
12199      * @cfg {String|Roo.Template} tpl The template used by this View 
12200      */
12201     tpl : false,
12202     /**
12203      * @cfg {String} dataName the named area of the template to use as the data area
12204      *                          Works with domtemplates roo-name="name"
12205      */
12206     dataName: false,
12207     /**
12208      * @cfg {String} selectedClass The css class to add to selected nodes
12209      */
12210     selectedClass : "x-view-selected",
12211      /**
12212      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12213      */
12214     emptyText : "",
12215     
12216     /**
12217      * @cfg {String} text to display on mask (default Loading)
12218      */
12219     mask : false,
12220     /**
12221      * @cfg {Boolean} multiSelect Allow multiple selection
12222      */
12223     multiSelect : false,
12224     /**
12225      * @cfg {Boolean} singleSelect Allow single selection
12226      */
12227     singleSelect:  false,
12228     
12229     /**
12230      * @cfg {Boolean} toggleSelect - selecting 
12231      */
12232     toggleSelect : false,
12233     
12234     /**
12235      * @cfg {Boolean} tickable - selecting 
12236      */
12237     tickable : false,
12238     
12239     /**
12240      * Returns the element this view is bound to.
12241      * @return {Roo.Element}
12242      */
12243     getEl : function(){
12244         return this.wrapEl;
12245     },
12246     
12247     
12248
12249     /**
12250      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12251      */
12252     refresh : function(){
12253         Roo.log('refresh');
12254         var t = this.tpl;
12255         
12256         // if we are using something like 'domtemplate', then
12257         // the what gets used is:
12258         // t.applySubtemplate(NAME, data, wrapping data..)
12259         // the outer template then get' applied with
12260         //     the store 'extra data'
12261         // and the body get's added to the
12262         //      roo-name="data" node?
12263         //      <span class='roo-tpl-{name}'></span> ?????
12264         
12265         
12266         
12267         this.clearSelections();
12268         this.el.update("");
12269         var html = [];
12270         var records = this.store.getRange();
12271         if(records.length < 1) {
12272             
12273             // is this valid??  = should it render a template??
12274             
12275             this.el.update(this.emptyText);
12276             return;
12277         }
12278         var el = this.el;
12279         if (this.dataName) {
12280             this.el.update(t.apply(this.store.meta)); //????
12281             el = this.el.child('.roo-tpl-' + this.dataName);
12282         }
12283         
12284         for(var i = 0, len = records.length; i < len; i++){
12285             var data = this.prepareData(records[i].data, i, records[i]);
12286             this.fireEvent("preparedata", this, data, i, records[i]);
12287             
12288             var d = Roo.apply({}, data);
12289             
12290             if(this.tickable){
12291                 Roo.apply(d, {'roo-id' : Roo.id()});
12292                 
12293                 var _this = this;
12294             
12295                 Roo.each(this.parent.item, function(item){
12296                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12297                         return;
12298                     }
12299                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12300                 });
12301             }
12302             
12303             html[html.length] = Roo.util.Format.trim(
12304                 this.dataName ?
12305                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12306                     t.apply(d)
12307             );
12308         }
12309         
12310         
12311         
12312         el.update(html.join(""));
12313         this.nodes = el.dom.childNodes;
12314         this.updateIndexes(0);
12315     },
12316     
12317
12318     /**
12319      * Function to override to reformat the data that is sent to
12320      * the template for each node.
12321      * DEPRICATED - use the preparedata event handler.
12322      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12323      * a JSON object for an UpdateManager bound view).
12324      */
12325     prepareData : function(data, index, record)
12326     {
12327         this.fireEvent("preparedata", this, data, index, record);
12328         return data;
12329     },
12330
12331     onUpdate : function(ds, record){
12332          Roo.log('on update');   
12333         this.clearSelections();
12334         var index = this.store.indexOf(record);
12335         var n = this.nodes[index];
12336         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12337         n.parentNode.removeChild(n);
12338         this.updateIndexes(index, index);
12339     },
12340
12341     
12342     
12343 // --------- FIXME     
12344     onAdd : function(ds, records, index)
12345     {
12346         Roo.log(['on Add', ds, records, index] );        
12347         this.clearSelections();
12348         if(this.nodes.length == 0){
12349             this.refresh();
12350             return;
12351         }
12352         var n = this.nodes[index];
12353         for(var i = 0, len = records.length; i < len; i++){
12354             var d = this.prepareData(records[i].data, i, records[i]);
12355             if(n){
12356                 this.tpl.insertBefore(n, d);
12357             }else{
12358                 
12359                 this.tpl.append(this.el, d);
12360             }
12361         }
12362         this.updateIndexes(index);
12363     },
12364
12365     onRemove : function(ds, record, index){
12366         Roo.log('onRemove');
12367         this.clearSelections();
12368         var el = this.dataName  ?
12369             this.el.child('.roo-tpl-' + this.dataName) :
12370             this.el; 
12371         
12372         el.dom.removeChild(this.nodes[index]);
12373         this.updateIndexes(index);
12374     },
12375
12376     /**
12377      * Refresh an individual node.
12378      * @param {Number} index
12379      */
12380     refreshNode : function(index){
12381         this.onUpdate(this.store, this.store.getAt(index));
12382     },
12383
12384     updateIndexes : function(startIndex, endIndex){
12385         var ns = this.nodes;
12386         startIndex = startIndex || 0;
12387         endIndex = endIndex || ns.length - 1;
12388         for(var i = startIndex; i <= endIndex; i++){
12389             ns[i].nodeIndex = i;
12390         }
12391     },
12392
12393     /**
12394      * Changes the data store this view uses and refresh the view.
12395      * @param {Store} store
12396      */
12397     setStore : function(store, initial){
12398         if(!initial && this.store){
12399             this.store.un("datachanged", this.refresh);
12400             this.store.un("add", this.onAdd);
12401             this.store.un("remove", this.onRemove);
12402             this.store.un("update", this.onUpdate);
12403             this.store.un("clear", this.refresh);
12404             this.store.un("beforeload", this.onBeforeLoad);
12405             this.store.un("load", this.onLoad);
12406             this.store.un("loadexception", this.onLoad);
12407         }
12408         if(store){
12409           
12410             store.on("datachanged", this.refresh, this);
12411             store.on("add", this.onAdd, this);
12412             store.on("remove", this.onRemove, this);
12413             store.on("update", this.onUpdate, this);
12414             store.on("clear", this.refresh, this);
12415             store.on("beforeload", this.onBeforeLoad, this);
12416             store.on("load", this.onLoad, this);
12417             store.on("loadexception", this.onLoad, this);
12418         }
12419         
12420         if(store){
12421             this.refresh();
12422         }
12423     },
12424     /**
12425      * onbeforeLoad - masks the loading area.
12426      *
12427      */
12428     onBeforeLoad : function(store,opts)
12429     {
12430          Roo.log('onBeforeLoad');   
12431         if (!opts.add) {
12432             this.el.update("");
12433         }
12434         this.el.mask(this.mask ? this.mask : "Loading" ); 
12435     },
12436     onLoad : function ()
12437     {
12438         this.el.unmask();
12439     },
12440     
12441
12442     /**
12443      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12444      * @param {HTMLElement} node
12445      * @return {HTMLElement} The template node
12446      */
12447     findItemFromChild : function(node){
12448         var el = this.dataName  ?
12449             this.el.child('.roo-tpl-' + this.dataName,true) :
12450             this.el.dom; 
12451         
12452         if(!node || node.parentNode == el){
12453                     return node;
12454             }
12455             var p = node.parentNode;
12456             while(p && p != el){
12457             if(p.parentNode == el){
12458                 return p;
12459             }
12460             p = p.parentNode;
12461         }
12462             return null;
12463     },
12464
12465     /** @ignore */
12466     onClick : function(e){
12467         var item = this.findItemFromChild(e.getTarget());
12468         if(item){
12469             var index = this.indexOf(item);
12470             if(this.onItemClick(item, index, e) !== false){
12471                 this.fireEvent("click", this, index, item, e);
12472             }
12473         }else{
12474             this.clearSelections();
12475         }
12476     },
12477
12478     /** @ignore */
12479     onContextMenu : function(e){
12480         var item = this.findItemFromChild(e.getTarget());
12481         if(item){
12482             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12483         }
12484     },
12485
12486     /** @ignore */
12487     onDblClick : function(e){
12488         var item = this.findItemFromChild(e.getTarget());
12489         if(item){
12490             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12491         }
12492     },
12493
12494     onItemClick : function(item, index, e)
12495     {
12496         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12497             return false;
12498         }
12499         if (this.toggleSelect) {
12500             var m = this.isSelected(item) ? 'unselect' : 'select';
12501             Roo.log(m);
12502             var _t = this;
12503             _t[m](item, true, false);
12504             return true;
12505         }
12506         if(this.multiSelect || this.singleSelect){
12507             if(this.multiSelect && e.shiftKey && this.lastSelection){
12508                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12509             }else{
12510                 this.select(item, this.multiSelect && e.ctrlKey);
12511                 this.lastSelection = item;
12512             }
12513             
12514             if(!this.tickable){
12515                 e.preventDefault();
12516             }
12517             
12518         }
12519         return true;
12520     },
12521
12522     /**
12523      * Get the number of selected nodes.
12524      * @return {Number}
12525      */
12526     getSelectionCount : function(){
12527         return this.selections.length;
12528     },
12529
12530     /**
12531      * Get the currently selected nodes.
12532      * @return {Array} An array of HTMLElements
12533      */
12534     getSelectedNodes : function(){
12535         return this.selections;
12536     },
12537
12538     /**
12539      * Get the indexes of the selected nodes.
12540      * @return {Array}
12541      */
12542     getSelectedIndexes : function(){
12543         var indexes = [], s = this.selections;
12544         for(var i = 0, len = s.length; i < len; i++){
12545             indexes.push(s[i].nodeIndex);
12546         }
12547         return indexes;
12548     },
12549
12550     /**
12551      * Clear all selections
12552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12553      */
12554     clearSelections : function(suppressEvent){
12555         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12556             this.cmp.elements = this.selections;
12557             this.cmp.removeClass(this.selectedClass);
12558             this.selections = [];
12559             if(!suppressEvent){
12560                 this.fireEvent("selectionchange", this, this.selections);
12561             }
12562         }
12563     },
12564
12565     /**
12566      * Returns true if the passed node is selected
12567      * @param {HTMLElement/Number} node The node or node index
12568      * @return {Boolean}
12569      */
12570     isSelected : function(node){
12571         var s = this.selections;
12572         if(s.length < 1){
12573             return false;
12574         }
12575         node = this.getNode(node);
12576         return s.indexOf(node) !== -1;
12577     },
12578
12579     /**
12580      * Selects nodes.
12581      * @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
12582      * @param {Boolean} keepExisting (optional) true to keep existing selections
12583      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12584      */
12585     select : function(nodeInfo, keepExisting, suppressEvent){
12586         if(nodeInfo instanceof Array){
12587             if(!keepExisting){
12588                 this.clearSelections(true);
12589             }
12590             for(var i = 0, len = nodeInfo.length; i < len; i++){
12591                 this.select(nodeInfo[i], true, true);
12592             }
12593             return;
12594         } 
12595         var node = this.getNode(nodeInfo);
12596         if(!node || this.isSelected(node)){
12597             return; // already selected.
12598         }
12599         if(!keepExisting){
12600             this.clearSelections(true);
12601         }
12602         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12603             Roo.fly(node).addClass(this.selectedClass);
12604             this.selections.push(node);
12605             if(!suppressEvent){
12606                 this.fireEvent("selectionchange", this, this.selections);
12607             }
12608         }
12609         
12610         
12611     },
12612       /**
12613      * Unselects nodes.
12614      * @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
12615      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12616      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12617      */
12618     unselect : function(nodeInfo, keepExisting, suppressEvent)
12619     {
12620         if(nodeInfo instanceof Array){
12621             Roo.each(this.selections, function(s) {
12622                 this.unselect(s, nodeInfo);
12623             }, this);
12624             return;
12625         }
12626         var node = this.getNode(nodeInfo);
12627         if(!node || !this.isSelected(node)){
12628             Roo.log("not selected");
12629             return; // not selected.
12630         }
12631         // fireevent???
12632         var ns = [];
12633         Roo.each(this.selections, function(s) {
12634             if (s == node ) {
12635                 Roo.fly(node).removeClass(this.selectedClass);
12636
12637                 return;
12638             }
12639             ns.push(s);
12640         },this);
12641         
12642         this.selections= ns;
12643         this.fireEvent("selectionchange", this, this.selections);
12644     },
12645
12646     /**
12647      * Gets a template node.
12648      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12649      * @return {HTMLElement} The node or null if it wasn't found
12650      */
12651     getNode : function(nodeInfo){
12652         if(typeof nodeInfo == "string"){
12653             return document.getElementById(nodeInfo);
12654         }else if(typeof nodeInfo == "number"){
12655             return this.nodes[nodeInfo];
12656         }
12657         return nodeInfo;
12658     },
12659
12660     /**
12661      * Gets a range template nodes.
12662      * @param {Number} startIndex
12663      * @param {Number} endIndex
12664      * @return {Array} An array of nodes
12665      */
12666     getNodes : function(start, end){
12667         var ns = this.nodes;
12668         start = start || 0;
12669         end = typeof end == "undefined" ? ns.length - 1 : end;
12670         var nodes = [];
12671         if(start <= end){
12672             for(var i = start; i <= end; i++){
12673                 nodes.push(ns[i]);
12674             }
12675         } else{
12676             for(var i = start; i >= end; i--){
12677                 nodes.push(ns[i]);
12678             }
12679         }
12680         return nodes;
12681     },
12682
12683     /**
12684      * Finds the index of the passed node
12685      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12686      * @return {Number} The index of the node or -1
12687      */
12688     indexOf : function(node){
12689         node = this.getNode(node);
12690         if(typeof node.nodeIndex == "number"){
12691             return node.nodeIndex;
12692         }
12693         var ns = this.nodes;
12694         for(var i = 0, len = ns.length; i < len; i++){
12695             if(ns[i] == node){
12696                 return i;
12697             }
12698         }
12699         return -1;
12700     }
12701 });
12702 /*
12703  * - LGPL
12704  *
12705  * based on jquery fullcalendar
12706  * 
12707  */
12708
12709 Roo.bootstrap = Roo.bootstrap || {};
12710 /**
12711  * @class Roo.bootstrap.Calendar
12712  * @extends Roo.bootstrap.Component
12713  * Bootstrap Calendar class
12714  * @cfg {Boolean} loadMask (true|false) default false
12715  * @cfg {Object} header generate the user specific header of the calendar, default false
12716
12717  * @constructor
12718  * Create a new Container
12719  * @param {Object} config The config object
12720  */
12721
12722
12723
12724 Roo.bootstrap.Calendar = function(config){
12725     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12726      this.addEvents({
12727         /**
12728              * @event select
12729              * Fires when a date is selected
12730              * @param {DatePicker} this
12731              * @param {Date} date The selected date
12732              */
12733         'select': true,
12734         /**
12735              * @event monthchange
12736              * Fires when the displayed month changes 
12737              * @param {DatePicker} this
12738              * @param {Date} date The selected month
12739              */
12740         'monthchange': true,
12741         /**
12742              * @event evententer
12743              * Fires when mouse over an event
12744              * @param {Calendar} this
12745              * @param {event} Event
12746              */
12747         'evententer': true,
12748         /**
12749              * @event eventleave
12750              * Fires when the mouse leaves an
12751              * @param {Calendar} this
12752              * @param {event}
12753              */
12754         'eventleave': true,
12755         /**
12756              * @event eventclick
12757              * Fires when the mouse click an
12758              * @param {Calendar} this
12759              * @param {event}
12760              */
12761         'eventclick': true
12762         
12763     });
12764
12765 };
12766
12767 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12768     
12769      /**
12770      * @cfg {Number} startDay
12771      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12772      */
12773     startDay : 0,
12774     
12775     loadMask : false,
12776     
12777     header : false,
12778       
12779     getAutoCreate : function(){
12780         
12781         
12782         var fc_button = function(name, corner, style, content ) {
12783             return Roo.apply({},{
12784                 tag : 'span',
12785                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12786                          (corner.length ?
12787                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12788                             ''
12789                         ),
12790                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12791                 unselectable: 'on'
12792             });
12793         };
12794         
12795         var header = {};
12796         
12797         if(!this.header){
12798             header = {
12799                 tag : 'table',
12800                 cls : 'fc-header',
12801                 style : 'width:100%',
12802                 cn : [
12803                     {
12804                         tag: 'tr',
12805                         cn : [
12806                             {
12807                                 tag : 'td',
12808                                 cls : 'fc-header-left',
12809                                 cn : [
12810                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12811                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12812                                     { tag: 'span', cls: 'fc-header-space' },
12813                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12814
12815
12816                                 ]
12817                             },
12818
12819                             {
12820                                 tag : 'td',
12821                                 cls : 'fc-header-center',
12822                                 cn : [
12823                                     {
12824                                         tag: 'span',
12825                                         cls: 'fc-header-title',
12826                                         cn : {
12827                                             tag: 'H2',
12828                                             html : 'month / year'
12829                                         }
12830                                     }
12831
12832                                 ]
12833                             },
12834                             {
12835                                 tag : 'td',
12836                                 cls : 'fc-header-right',
12837                                 cn : [
12838                               /*      fc_button('month', 'left', '', 'month' ),
12839                                     fc_button('week', '', '', 'week' ),
12840                                     fc_button('day', 'right', '', 'day' )
12841                                 */    
12842
12843                                 ]
12844                             }
12845
12846                         ]
12847                     }
12848                 ]
12849             };
12850         }
12851         
12852         header = this.header;
12853         
12854        
12855         var cal_heads = function() {
12856             var ret = [];
12857             // fixme - handle this.
12858             
12859             for (var i =0; i < Date.dayNames.length; i++) {
12860                 var d = Date.dayNames[i];
12861                 ret.push({
12862                     tag: 'th',
12863                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12864                     html : d.substring(0,3)
12865                 });
12866                 
12867             }
12868             ret[0].cls += ' fc-first';
12869             ret[6].cls += ' fc-last';
12870             return ret;
12871         };
12872         var cal_cell = function(n) {
12873             return  {
12874                 tag: 'td',
12875                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12876                 cn : [
12877                     {
12878                         cn : [
12879                             {
12880                                 cls: 'fc-day-number',
12881                                 html: 'D'
12882                             },
12883                             {
12884                                 cls: 'fc-day-content',
12885                              
12886                                 cn : [
12887                                      {
12888                                         style: 'position: relative;' // height: 17px;
12889                                     }
12890                                 ]
12891                             }
12892                             
12893                             
12894                         ]
12895                     }
12896                 ]
12897                 
12898             }
12899         };
12900         var cal_rows = function() {
12901             
12902             var ret = []
12903             for (var r = 0; r < 6; r++) {
12904                 var row= {
12905                     tag : 'tr',
12906                     cls : 'fc-week',
12907                     cn : []
12908                 };
12909                 
12910                 for (var i =0; i < Date.dayNames.length; i++) {
12911                     var d = Date.dayNames[i];
12912                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12913
12914                 }
12915                 row.cn[0].cls+=' fc-first';
12916                 row.cn[0].cn[0].style = 'min-height:90px';
12917                 row.cn[6].cls+=' fc-last';
12918                 ret.push(row);
12919                 
12920             }
12921             ret[0].cls += ' fc-first';
12922             ret[4].cls += ' fc-prev-last';
12923             ret[5].cls += ' fc-last';
12924             return ret;
12925             
12926         };
12927         
12928         var cal_table = {
12929             tag: 'table',
12930             cls: 'fc-border-separate',
12931             style : 'width:100%',
12932             cellspacing  : 0,
12933             cn : [
12934                 { 
12935                     tag: 'thead',
12936                     cn : [
12937                         { 
12938                             tag: 'tr',
12939                             cls : 'fc-first fc-last',
12940                             cn : cal_heads()
12941                         }
12942                     ]
12943                 },
12944                 { 
12945                     tag: 'tbody',
12946                     cn : cal_rows()
12947                 }
12948                   
12949             ]
12950         };
12951          
12952          var cfg = {
12953             cls : 'fc fc-ltr',
12954             cn : [
12955                 header,
12956                 {
12957                     cls : 'fc-content',
12958                     style : "position: relative;",
12959                     cn : [
12960                         {
12961                             cls : 'fc-view fc-view-month fc-grid',
12962                             style : 'position: relative',
12963                             unselectable : 'on',
12964                             cn : [
12965                                 {
12966                                     cls : 'fc-event-container',
12967                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12968                                 },
12969                                 cal_table
12970                             ]
12971                         }
12972                     ]
12973     
12974                 }
12975            ] 
12976             
12977         };
12978         
12979          
12980         
12981         return cfg;
12982     },
12983     
12984     
12985     initEvents : function()
12986     {
12987         if(!this.store){
12988             throw "can not find store for calendar";
12989         }
12990         
12991         var mark = {
12992             tag: "div",
12993             cls:"x-dlg-mask",
12994             style: "text-align:center",
12995             cn: [
12996                 {
12997                     tag: "div",
12998                     style: "background-color:white;width:50%;margin:250 auto",
12999                     cn: [
13000                         {
13001                             tag: "img",
13002                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13003                         },
13004                         {
13005                             tag: "span",
13006                             html: "Loading"
13007                         }
13008                         
13009                     ]
13010                 }
13011             ]
13012         }
13013         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13014         
13015         var size = this.el.select('.fc-content', true).first().getSize();
13016         this.maskEl.setSize(size.width, size.height);
13017         this.maskEl.enableDisplayMode("block");
13018         if(!this.loadMask){
13019             this.maskEl.hide();
13020         }
13021         
13022         this.store = Roo.factory(this.store, Roo.data);
13023         this.store.on('load', this.onLoad, this);
13024         this.store.on('beforeload', this.onBeforeLoad, this);
13025         
13026         this.resize();
13027         
13028         this.cells = this.el.select('.fc-day',true);
13029         //Roo.log(this.cells);
13030         this.textNodes = this.el.query('.fc-day-number');
13031         this.cells.addClassOnOver('fc-state-hover');
13032         
13033         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13034         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13035         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13036         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13037         
13038         this.on('monthchange', this.onMonthChange, this);
13039         
13040         this.update(new Date().clearTime());
13041     },
13042     
13043     resize : function() {
13044         var sz  = this.el.getSize();
13045         
13046         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13047         this.el.select('.fc-day-content div',true).setHeight(34);
13048     },
13049     
13050     
13051     // private
13052     showPrevMonth : function(e){
13053         this.update(this.activeDate.add("mo", -1));
13054     },
13055     showToday : function(e){
13056         this.update(new Date().clearTime());
13057     },
13058     // private
13059     showNextMonth : function(e){
13060         this.update(this.activeDate.add("mo", 1));
13061     },
13062
13063     // private
13064     showPrevYear : function(){
13065         this.update(this.activeDate.add("y", -1));
13066     },
13067
13068     // private
13069     showNextYear : function(){
13070         this.update(this.activeDate.add("y", 1));
13071     },
13072
13073     
13074    // private
13075     update : function(date)
13076     {
13077         var vd = this.activeDate;
13078         this.activeDate = date;
13079 //        if(vd && this.el){
13080 //            var t = date.getTime();
13081 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13082 //                Roo.log('using add remove');
13083 //                
13084 //                this.fireEvent('monthchange', this, date);
13085 //                
13086 //                this.cells.removeClass("fc-state-highlight");
13087 //                this.cells.each(function(c){
13088 //                   if(c.dateValue == t){
13089 //                       c.addClass("fc-state-highlight");
13090 //                       setTimeout(function(){
13091 //                            try{c.dom.firstChild.focus();}catch(e){}
13092 //                       }, 50);
13093 //                       return false;
13094 //                   }
13095 //                   return true;
13096 //                });
13097 //                return;
13098 //            }
13099 //        }
13100         
13101         var days = date.getDaysInMonth();
13102         
13103         var firstOfMonth = date.getFirstDateOfMonth();
13104         var startingPos = firstOfMonth.getDay()-this.startDay;
13105         
13106         if(startingPos < this.startDay){
13107             startingPos += 7;
13108         }
13109         
13110         var pm = date.add(Date.MONTH, -1);
13111         var prevStart = pm.getDaysInMonth()-startingPos;
13112 //        
13113         this.cells = this.el.select('.fc-day',true);
13114         this.textNodes = this.el.query('.fc-day-number');
13115         this.cells.addClassOnOver('fc-state-hover');
13116         
13117         var cells = this.cells.elements;
13118         var textEls = this.textNodes;
13119         
13120         Roo.each(cells, function(cell){
13121             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13122         });
13123         
13124         days += startingPos;
13125
13126         // convert everything to numbers so it's fast
13127         var day = 86400000;
13128         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13129         //Roo.log(d);
13130         //Roo.log(pm);
13131         //Roo.log(prevStart);
13132         
13133         var today = new Date().clearTime().getTime();
13134         var sel = date.clearTime().getTime();
13135         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13136         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13137         var ddMatch = this.disabledDatesRE;
13138         var ddText = this.disabledDatesText;
13139         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13140         var ddaysText = this.disabledDaysText;
13141         var format = this.format;
13142         
13143         var setCellClass = function(cal, cell){
13144             cell.row = 0;
13145             cell.events = [];
13146             cell.more = [];
13147             //Roo.log('set Cell Class');
13148             cell.title = "";
13149             var t = d.getTime();
13150             
13151             //Roo.log(d);
13152             
13153             cell.dateValue = t;
13154             if(t == today){
13155                 cell.className += " fc-today";
13156                 cell.className += " fc-state-highlight";
13157                 cell.title = cal.todayText;
13158             }
13159             if(t == sel){
13160                 // disable highlight in other month..
13161                 //cell.className += " fc-state-highlight";
13162                 
13163             }
13164             // disabling
13165             if(t < min) {
13166                 cell.className = " fc-state-disabled";
13167                 cell.title = cal.minText;
13168                 return;
13169             }
13170             if(t > max) {
13171                 cell.className = " fc-state-disabled";
13172                 cell.title = cal.maxText;
13173                 return;
13174             }
13175             if(ddays){
13176                 if(ddays.indexOf(d.getDay()) != -1){
13177                     cell.title = ddaysText;
13178                     cell.className = " fc-state-disabled";
13179                 }
13180             }
13181             if(ddMatch && format){
13182                 var fvalue = d.dateFormat(format);
13183                 if(ddMatch.test(fvalue)){
13184                     cell.title = ddText.replace("%0", fvalue);
13185                     cell.className = " fc-state-disabled";
13186                 }
13187             }
13188             
13189             if (!cell.initialClassName) {
13190                 cell.initialClassName = cell.dom.className;
13191             }
13192             
13193             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13194         };
13195
13196         var i = 0;
13197         
13198         for(; i < startingPos; i++) {
13199             textEls[i].innerHTML = (++prevStart);
13200             d.setDate(d.getDate()+1);
13201             
13202             cells[i].className = "fc-past fc-other-month";
13203             setCellClass(this, cells[i]);
13204         }
13205         
13206         var intDay = 0;
13207         
13208         for(; i < days; i++){
13209             intDay = i - startingPos + 1;
13210             textEls[i].innerHTML = (intDay);
13211             d.setDate(d.getDate()+1);
13212             
13213             cells[i].className = ''; // "x-date-active";
13214             setCellClass(this, cells[i]);
13215         }
13216         var extraDays = 0;
13217         
13218         for(; i < 42; i++) {
13219             textEls[i].innerHTML = (++extraDays);
13220             d.setDate(d.getDate()+1);
13221             
13222             cells[i].className = "fc-future fc-other-month";
13223             setCellClass(this, cells[i]);
13224         }
13225         
13226         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13227         
13228         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13229         
13230         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13231         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13232         
13233         if(totalRows != 6){
13234             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13235             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13236         }
13237         
13238         this.fireEvent('monthchange', this, date);
13239         
13240         
13241         /*
13242         if(!this.internalRender){
13243             var main = this.el.dom.firstChild;
13244             var w = main.offsetWidth;
13245             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13246             Roo.fly(main).setWidth(w);
13247             this.internalRender = true;
13248             // opera does not respect the auto grow header center column
13249             // then, after it gets a width opera refuses to recalculate
13250             // without a second pass
13251             if(Roo.isOpera && !this.secondPass){
13252                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13253                 this.secondPass = true;
13254                 this.update.defer(10, this, [date]);
13255             }
13256         }
13257         */
13258         
13259     },
13260     
13261     findCell : function(dt) {
13262         dt = dt.clearTime().getTime();
13263         var ret = false;
13264         this.cells.each(function(c){
13265             //Roo.log("check " +c.dateValue + '?=' + dt);
13266             if(c.dateValue == dt){
13267                 ret = c;
13268                 return false;
13269             }
13270             return true;
13271         });
13272         
13273         return ret;
13274     },
13275     
13276     findCells : function(ev) {
13277         var s = ev.start.clone().clearTime().getTime();
13278        // Roo.log(s);
13279         var e= ev.end.clone().clearTime().getTime();
13280        // Roo.log(e);
13281         var ret = [];
13282         this.cells.each(function(c){
13283              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13284             
13285             if(c.dateValue > e){
13286                 return ;
13287             }
13288             if(c.dateValue < s){
13289                 return ;
13290             }
13291             ret.push(c);
13292         });
13293         
13294         return ret;    
13295     },
13296     
13297 //    findBestRow: function(cells)
13298 //    {
13299 //        var ret = 0;
13300 //        
13301 //        for (var i =0 ; i < cells.length;i++) {
13302 //            ret  = Math.max(cells[i].rows || 0,ret);
13303 //        }
13304 //        return ret;
13305 //        
13306 //    },
13307     
13308     
13309     addItem : function(ev)
13310     {
13311         // look for vertical location slot in
13312         var cells = this.findCells(ev);
13313         
13314 //        ev.row = this.findBestRow(cells);
13315         
13316         // work out the location.
13317         
13318         var crow = false;
13319         var rows = [];
13320         for(var i =0; i < cells.length; i++) {
13321             
13322             cells[i].row = cells[0].row;
13323             
13324             if(i == 0){
13325                 cells[i].row = cells[i].row + 1;
13326             }
13327             
13328             if (!crow) {
13329                 crow = {
13330                     start : cells[i],
13331                     end :  cells[i]
13332                 };
13333                 continue;
13334             }
13335             if (crow.start.getY() == cells[i].getY()) {
13336                 // on same row.
13337                 crow.end = cells[i];
13338                 continue;
13339             }
13340             // different row.
13341             rows.push(crow);
13342             crow = {
13343                 start: cells[i],
13344                 end : cells[i]
13345             };
13346             
13347         }
13348         
13349         rows.push(crow);
13350         ev.els = [];
13351         ev.rows = rows;
13352         ev.cells = cells;
13353         
13354         cells[0].events.push(ev);
13355         
13356         this.calevents.push(ev);
13357     },
13358     
13359     clearEvents: function() {
13360         
13361         if(!this.calevents){
13362             return;
13363         }
13364         
13365         Roo.each(this.cells.elements, function(c){
13366             c.row = 0;
13367             c.events = [];
13368             c.more = [];
13369         });
13370         
13371         Roo.each(this.calevents, function(e) {
13372             Roo.each(e.els, function(el) {
13373                 el.un('mouseenter' ,this.onEventEnter, this);
13374                 el.un('mouseleave' ,this.onEventLeave, this);
13375                 el.remove();
13376             },this);
13377         },this);
13378         
13379         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13380             e.remove();
13381         });
13382         
13383     },
13384     
13385     renderEvents: function()
13386     {   
13387         var _this = this;
13388         
13389         this.cells.each(function(c) {
13390             
13391             if(c.row < 5){
13392                 return;
13393             }
13394             
13395             var ev = c.events;
13396             
13397             var r = 4;
13398             if(c.row != c.events.length){
13399                 r = 4 - (4 - (c.row - c.events.length));
13400             }
13401             
13402             c.events = ev.slice(0, r);
13403             c.more = ev.slice(r);
13404             
13405             if(c.more.length && c.more.length == 1){
13406                 c.events.push(c.more.pop());
13407             }
13408             
13409             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13410             
13411         });
13412             
13413         this.cells.each(function(c) {
13414             
13415             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13416             
13417             
13418             for (var e = 0; e < c.events.length; e++){
13419                 var ev = c.events[e];
13420                 var rows = ev.rows;
13421                 
13422                 for(var i = 0; i < rows.length; i++) {
13423                 
13424                     // how many rows should it span..
13425
13426                     var  cfg = {
13427                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13428                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13429
13430                         unselectable : "on",
13431                         cn : [
13432                             {
13433                                 cls: 'fc-event-inner',
13434                                 cn : [
13435     //                                {
13436     //                                  tag:'span',
13437     //                                  cls: 'fc-event-time',
13438     //                                  html : cells.length > 1 ? '' : ev.time
13439     //                                },
13440                                     {
13441                                       tag:'span',
13442                                       cls: 'fc-event-title',
13443                                       html : String.format('{0}', ev.title)
13444                                     }
13445
13446
13447                                 ]
13448                             },
13449                             {
13450                                 cls: 'ui-resizable-handle ui-resizable-e',
13451                                 html : '&nbsp;&nbsp;&nbsp'
13452                             }
13453
13454                         ]
13455                     };
13456
13457                     if (i == 0) {
13458                         cfg.cls += ' fc-event-start';
13459                     }
13460                     if ((i+1) == rows.length) {
13461                         cfg.cls += ' fc-event-end';
13462                     }
13463
13464                     var ctr = _this.el.select('.fc-event-container',true).first();
13465                     var cg = ctr.createChild(cfg);
13466
13467                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13468                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13469
13470                     var r = (c.more.length) ? 1 : 0;
13471                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13472                     cg.setWidth(ebox.right - sbox.x -2);
13473
13474                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13475                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13476                     cg.on('click', _this.onEventClick, _this, ev);
13477
13478                     ev.els.push(cg);
13479                     
13480                 }
13481                 
13482             }
13483             
13484             
13485             if(c.more.length){
13486                 var  cfg = {
13487                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13488                     style : 'position: absolute',
13489                     unselectable : "on",
13490                     cn : [
13491                         {
13492                             cls: 'fc-event-inner',
13493                             cn : [
13494                                 {
13495                                   tag:'span',
13496                                   cls: 'fc-event-title',
13497                                   html : 'More'
13498                                 }
13499
13500
13501                             ]
13502                         },
13503                         {
13504                             cls: 'ui-resizable-handle ui-resizable-e',
13505                             html : '&nbsp;&nbsp;&nbsp'
13506                         }
13507
13508                     ]
13509                 };
13510
13511                 var ctr = _this.el.select('.fc-event-container',true).first();
13512                 var cg = ctr.createChild(cfg);
13513
13514                 var sbox = c.select('.fc-day-content',true).first().getBox();
13515                 var ebox = c.select('.fc-day-content',true).first().getBox();
13516                 //Roo.log(cg);
13517                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13518                 cg.setWidth(ebox.right - sbox.x -2);
13519
13520                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13521                 
13522             }
13523             
13524         });
13525         
13526         
13527         
13528     },
13529     
13530     onEventEnter: function (e, el,event,d) {
13531         this.fireEvent('evententer', this, el, event);
13532     },
13533     
13534     onEventLeave: function (e, el,event,d) {
13535         this.fireEvent('eventleave', this, el, event);
13536     },
13537     
13538     onEventClick: function (e, el,event,d) {
13539         this.fireEvent('eventclick', this, el, event);
13540     },
13541     
13542     onMonthChange: function () {
13543         this.store.load();
13544     },
13545     
13546     onMoreEventClick: function(e, el, more)
13547     {
13548         var _this = this;
13549         
13550         this.calpopover.placement = 'right';
13551         this.calpopover.setTitle('More');
13552         
13553         this.calpopover.setContent('');
13554         
13555         var ctr = this.calpopover.el.select('.popover-content', true).first();
13556         
13557         Roo.each(more, function(m){
13558             var cfg = {
13559                 cls : 'fc-event-hori fc-event-draggable',
13560                 html : m.title
13561             }
13562             var cg = ctr.createChild(cfg);
13563             
13564             cg.on('click', _this.onEventClick, _this, m);
13565         });
13566         
13567         this.calpopover.show(el);
13568         
13569         
13570     },
13571     
13572     onLoad: function () 
13573     {   
13574         this.calevents = [];
13575         var cal = this;
13576         
13577         if(this.store.getCount() > 0){
13578             this.store.data.each(function(d){
13579                cal.addItem({
13580                     id : d.data.id,
13581                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13582                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13583                     time : d.data.start_time,
13584                     title : d.data.title,
13585                     description : d.data.description,
13586                     venue : d.data.venue
13587                 });
13588             });
13589         }
13590         
13591         this.renderEvents();
13592         
13593         if(this.calevents.length && this.loadMask){
13594             this.maskEl.hide();
13595         }
13596     },
13597     
13598     onBeforeLoad: function()
13599     {
13600         this.clearEvents();
13601         if(this.loadMask){
13602             this.maskEl.show();
13603         }
13604     }
13605 });
13606
13607  
13608  /*
13609  * - LGPL
13610  *
13611  * element
13612  * 
13613  */
13614
13615 /**
13616  * @class Roo.bootstrap.Popover
13617  * @extends Roo.bootstrap.Component
13618  * Bootstrap Popover class
13619  * @cfg {String} html contents of the popover   (or false to use children..)
13620  * @cfg {String} title of popover (or false to hide)
13621  * @cfg {String} placement how it is placed
13622  * @cfg {String} trigger click || hover (or false to trigger manually)
13623  * @cfg {String} over what (parent or false to trigger manually.)
13624  * 
13625  * @constructor
13626  * Create a new Popover
13627  * @param {Object} config The config object
13628  */
13629
13630 Roo.bootstrap.Popover = function(config){
13631     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13632 };
13633
13634 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13635     
13636     title: 'Fill in a title',
13637     html: false,
13638     
13639     placement : 'right',
13640     trigger : 'hover', // hover
13641     
13642     over: 'parent',
13643     
13644     can_build_overlaid : false,
13645     
13646     getChildContainer : function()
13647     {
13648         return this.el.select('.popover-content',true).first();
13649     },
13650     
13651     getAutoCreate : function(){
13652          Roo.log('make popover?');
13653         var cfg = {
13654            cls : 'popover roo-dynamic',
13655            style: 'display:block',
13656            cn : [
13657                 {
13658                     cls : 'arrow'
13659                 },
13660                 {
13661                     cls : 'popover-inner',
13662                     cn : [
13663                         {
13664                             tag: 'h3',
13665                             cls: 'popover-title',
13666                             html : this.title
13667                         },
13668                         {
13669                             cls : 'popover-content',
13670                             html : this.html
13671                         }
13672                     ]
13673                     
13674                 }
13675            ]
13676         };
13677         
13678         return cfg;
13679     },
13680     setTitle: function(str)
13681     {
13682         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13683     },
13684     setContent: function(str)
13685     {
13686         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13687     },
13688     // as it get's added to the bottom of the page.
13689     onRender : function(ct, position)
13690     {
13691         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13692         if(!this.el){
13693             var cfg = Roo.apply({},  this.getAutoCreate());
13694             cfg.id = Roo.id();
13695             
13696             if (this.cls) {
13697                 cfg.cls += ' ' + this.cls;
13698             }
13699             if (this.style) {
13700                 cfg.style = this.style;
13701             }
13702             Roo.log("adding to ")
13703             this.el = Roo.get(document.body).createChild(cfg, position);
13704             Roo.log(this.el);
13705         }
13706         this.initEvents();
13707     },
13708     
13709     initEvents : function()
13710     {
13711         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13712         this.el.enableDisplayMode('block');
13713         this.el.hide();
13714         if (this.over === false) {
13715             return; 
13716         }
13717         if (this.triggers === false) {
13718             return;
13719         }
13720         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13721         var triggers = this.trigger ? this.trigger.split(' ') : [];
13722         Roo.each(triggers, function(trigger) {
13723         
13724             if (trigger == 'click') {
13725                 on_el.on('click', this.toggle, this);
13726             } else if (trigger != 'manual') {
13727                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13728                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13729       
13730                 on_el.on(eventIn  ,this.enter, this);
13731                 on_el.on(eventOut, this.leave, this);
13732             }
13733         }, this);
13734         
13735     },
13736     
13737     
13738     // private
13739     timeout : null,
13740     hoverState : null,
13741     
13742     toggle : function () {
13743         this.hoverState == 'in' ? this.leave() : this.enter();
13744     },
13745     
13746     enter : function () {
13747        
13748     
13749         clearTimeout(this.timeout);
13750     
13751         this.hoverState = 'in'
13752     
13753         if (!this.delay || !this.delay.show) {
13754             this.show();
13755             return 
13756         }
13757         var _t = this;
13758         this.timeout = setTimeout(function () {
13759             if (_t.hoverState == 'in') {
13760                 _t.show();
13761             }
13762         }, this.delay.show)
13763     },
13764     leave : function() {
13765         clearTimeout(this.timeout);
13766     
13767         this.hoverState = 'out'
13768     
13769         if (!this.delay || !this.delay.hide) {
13770             this.hide();
13771             return 
13772         }
13773         var _t = this;
13774         this.timeout = setTimeout(function () {
13775             if (_t.hoverState == 'out') {
13776                 _t.hide();
13777             }
13778         }, this.delay.hide)
13779     },
13780     
13781     show : function (on_el)
13782     {
13783         if (!on_el) {
13784             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13785         }
13786         // set content.
13787         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13788         if (this.html !== false) {
13789             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13790         }
13791         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13792         if (!this.title.length) {
13793             this.el.select('.popover-title',true).hide();
13794         }
13795         
13796         var placement = typeof this.placement == 'function' ?
13797             this.placement.call(this, this.el, on_el) :
13798             this.placement;
13799             
13800         var autoToken = /\s?auto?\s?/i;
13801         var autoPlace = autoToken.test(placement);
13802         if (autoPlace) {
13803             placement = placement.replace(autoToken, '') || 'top';
13804         }
13805         
13806         //this.el.detach()
13807         //this.el.setXY([0,0]);
13808         this.el.show();
13809         this.el.dom.style.display='block';
13810         this.el.addClass(placement);
13811         
13812         //this.el.appendTo(on_el);
13813         
13814         var p = this.getPosition();
13815         var box = this.el.getBox();
13816         
13817         if (autoPlace) {
13818             // fixme..
13819         }
13820         var align = Roo.bootstrap.Popover.alignment[placement]
13821         this.el.alignTo(on_el, align[0],align[1]);
13822         //var arrow = this.el.select('.arrow',true).first();
13823         //arrow.set(align[2], 
13824         
13825         this.el.addClass('in');
13826         this.hoverState = null;
13827         
13828         if (this.el.hasClass('fade')) {
13829             // fade it?
13830         }
13831         
13832     },
13833     hide : function()
13834     {
13835         this.el.setXY([0,0]);
13836         this.el.removeClass('in');
13837         this.el.hide();
13838         
13839     }
13840     
13841 });
13842
13843 Roo.bootstrap.Popover.alignment = {
13844     'left' : ['r-l', [-10,0], 'right'],
13845     'right' : ['l-r', [10,0], 'left'],
13846     'bottom' : ['t-b', [0,10], 'top'],
13847     'top' : [ 'b-t', [0,-10], 'bottom']
13848 };
13849
13850  /*
13851  * - LGPL
13852  *
13853  * Progress
13854  * 
13855  */
13856
13857 /**
13858  * @class Roo.bootstrap.Progress
13859  * @extends Roo.bootstrap.Component
13860  * Bootstrap Progress class
13861  * @cfg {Boolean} striped striped of the progress bar
13862  * @cfg {Boolean} active animated of the progress bar
13863  * 
13864  * 
13865  * @constructor
13866  * Create a new Progress
13867  * @param {Object} config The config object
13868  */
13869
13870 Roo.bootstrap.Progress = function(config){
13871     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13872 };
13873
13874 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13875     
13876     striped : false,
13877     active: false,
13878     
13879     getAutoCreate : function(){
13880         var cfg = {
13881             tag: 'div',
13882             cls: 'progress'
13883         };
13884         
13885         
13886         if(this.striped){
13887             cfg.cls += ' progress-striped';
13888         }
13889       
13890         if(this.active){
13891             cfg.cls += ' active';
13892         }
13893         
13894         
13895         return cfg;
13896     }
13897    
13898 });
13899
13900  
13901
13902  /*
13903  * - LGPL
13904  *
13905  * ProgressBar
13906  * 
13907  */
13908
13909 /**
13910  * @class Roo.bootstrap.ProgressBar
13911  * @extends Roo.bootstrap.Component
13912  * Bootstrap ProgressBar class
13913  * @cfg {Number} aria_valuenow aria-value now
13914  * @cfg {Number} aria_valuemin aria-value min
13915  * @cfg {Number} aria_valuemax aria-value max
13916  * @cfg {String} label label for the progress bar
13917  * @cfg {String} panel (success | info | warning | danger )
13918  * @cfg {String} role role of the progress bar
13919  * @cfg {String} sr_only text
13920  * 
13921  * 
13922  * @constructor
13923  * Create a new ProgressBar
13924  * @param {Object} config The config object
13925  */
13926
13927 Roo.bootstrap.ProgressBar = function(config){
13928     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13929 };
13930
13931 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13932     
13933     aria_valuenow : 0,
13934     aria_valuemin : 0,
13935     aria_valuemax : 100,
13936     label : false,
13937     panel : false,
13938     role : false,
13939     sr_only: false,
13940     
13941     getAutoCreate : function()
13942     {
13943         
13944         var cfg = {
13945             tag: 'div',
13946             cls: 'progress-bar',
13947             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13948         };
13949         
13950         if(this.sr_only){
13951             cfg.cn = {
13952                 tag: 'span',
13953                 cls: 'sr-only',
13954                 html: this.sr_only
13955             }
13956         }
13957         
13958         if(this.role){
13959             cfg.role = this.role;
13960         }
13961         
13962         if(this.aria_valuenow){
13963             cfg['aria-valuenow'] = this.aria_valuenow;
13964         }
13965         
13966         if(this.aria_valuemin){
13967             cfg['aria-valuemin'] = this.aria_valuemin;
13968         }
13969         
13970         if(this.aria_valuemax){
13971             cfg['aria-valuemax'] = this.aria_valuemax;
13972         }
13973         
13974         if(this.label && !this.sr_only){
13975             cfg.html = this.label;
13976         }
13977         
13978         if(this.panel){
13979             cfg.cls += ' progress-bar-' + this.panel;
13980         }
13981         
13982         return cfg;
13983     },
13984     
13985     update : function(aria_valuenow)
13986     {
13987         this.aria_valuenow = aria_valuenow;
13988         
13989         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13990     }
13991    
13992 });
13993
13994  
13995
13996  /*
13997  * - LGPL
13998  *
13999  * column
14000  * 
14001  */
14002
14003 /**
14004  * @class Roo.bootstrap.TabGroup
14005  * @extends Roo.bootstrap.Column
14006  * Bootstrap Column class
14007  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14008  * @cfg {Boolean} carousel true to make the group behave like a carousel
14009  * 
14010  * @constructor
14011  * Create a new TabGroup
14012  * @param {Object} config The config object
14013  */
14014
14015 Roo.bootstrap.TabGroup = function(config){
14016     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14017     if (!this.navId) {
14018         this.navId = Roo.id();
14019     }
14020     this.tabs = [];
14021     Roo.bootstrap.TabGroup.register(this);
14022     
14023 };
14024
14025 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14026     
14027     carousel : false,
14028     transition : false,
14029      
14030     getAutoCreate : function()
14031     {
14032         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14033         
14034         cfg.cls += ' tab-content';
14035         
14036         if (this.carousel) {
14037             cfg.cls += ' carousel slide';
14038             cfg.cn = [{
14039                cls : 'carousel-inner'
14040             }]
14041         }
14042         
14043         
14044         return cfg;
14045     },
14046     getChildContainer : function()
14047     {
14048         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14049     },
14050     
14051     /**
14052     * register a Navigation item
14053     * @param {Roo.bootstrap.NavItem} the navitem to add
14054     */
14055     register : function(item)
14056     {
14057         this.tabs.push( item);
14058         item.navId = this.navId; // not really needed..
14059     
14060     },
14061     
14062     getActivePanel : function()
14063     {
14064         var r = false;
14065         Roo.each(this.tabs, function(t) {
14066             if (t.active) {
14067                 r = t;
14068                 return false;
14069             }
14070             return null;
14071         });
14072         return r;
14073         
14074     },
14075     getPanelByName : function(n)
14076     {
14077         var r = false;
14078         Roo.each(this.tabs, function(t) {
14079             if (t.tabId == n) {
14080                 r = t;
14081                 return false;
14082             }
14083             return null;
14084         });
14085         return r;
14086     },
14087     indexOfPanel : function(p)
14088     {
14089         var r = false;
14090         Roo.each(this.tabs, function(t,i) {
14091             if (t.tabId == p.tabId) {
14092                 r = i;
14093                 return false;
14094             }
14095             return null;
14096         });
14097         return r;
14098     },
14099     /**
14100      * show a specific panel
14101      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14102      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14103      */
14104     showPanel : function (pan)
14105     {
14106         
14107         if (typeof(pan) == 'number') {
14108             pan = this.tabs[pan];
14109         }
14110         if (typeof(pan) == 'string') {
14111             pan = this.getPanelByName(pan);
14112         }
14113         if (pan.tabId == this.getActivePanel().tabId) {
14114             return true;
14115         }
14116         var cur = this.getActivePanel();
14117         
14118         if (false === cur.fireEvent('beforedeactivate')) {
14119             return false;
14120         }
14121         
14122         if (this.carousel) {
14123             this.transition = true;
14124             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14125             var lr = dir == 'next' ? 'left' : 'right';
14126             pan.el.addClass(dir); // or prev
14127             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14128             cur.el.addClass(lr); // or right
14129             pan.el.addClass(lr);
14130             
14131             var _this = this;
14132             cur.el.on('transitionend', function() {
14133                 Roo.log("trans end?");
14134                 
14135                 pan.el.removeClass([lr,dir]);
14136                 pan.setActive(true);
14137                 
14138                 cur.el.removeClass([lr]);
14139                 cur.setActive(false);
14140                 
14141                 _this.transition = false;
14142                 
14143             }, this, { single:  true } );
14144             return true;
14145         }
14146         
14147         cur.setActive(false);
14148         pan.setActive(true);
14149         return true;
14150         
14151     },
14152     showPanelNext : function()
14153     {
14154         var i = this.indexOfPanel(this.getActivePanel());
14155         if (i > this.tabs.length) {
14156             return;
14157         }
14158         this.showPanel(this.tabs[i+1]);
14159     },
14160     showPanelPrev : function()
14161     {
14162         var i = this.indexOfPanel(this.getActivePanel());
14163         if (i  < 1) {
14164             return;
14165         }
14166         this.showPanel(this.tabs[i-1]);
14167     }
14168     
14169     
14170   
14171 });
14172
14173  
14174
14175  
14176  
14177 Roo.apply(Roo.bootstrap.TabGroup, {
14178     
14179     groups: {},
14180      /**
14181     * register a Navigation Group
14182     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14183     */
14184     register : function(navgrp)
14185     {
14186         this.groups[navgrp.navId] = navgrp;
14187         
14188     },
14189     /**
14190     * fetch a Navigation Group based on the navigation ID
14191     * if one does not exist , it will get created.
14192     * @param {string} the navgroup to add
14193     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14194     */
14195     get: function(navId) {
14196         if (typeof(this.groups[navId]) == 'undefined') {
14197             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14198         }
14199         return this.groups[navId] ;
14200     }
14201     
14202     
14203     
14204 });
14205
14206  /*
14207  * - LGPL
14208  *
14209  * TabPanel
14210  * 
14211  */
14212
14213 /**
14214  * @class Roo.bootstrap.TabPanel
14215  * @extends Roo.bootstrap.Component
14216  * Bootstrap TabPanel class
14217  * @cfg {Boolean} active panel active
14218  * @cfg {String} html panel content
14219  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14220  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14221  * 
14222  * 
14223  * @constructor
14224  * Create a new TabPanel
14225  * @param {Object} config The config object
14226  */
14227
14228 Roo.bootstrap.TabPanel = function(config){
14229     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14230     this.addEvents({
14231         /**
14232              * @event changed
14233              * Fires when the active status changes
14234              * @param {Roo.bootstrap.TabPanel} this
14235              * @param {Boolean} state the new state
14236             
14237          */
14238         'changed': true,
14239         /**
14240              * @event beforedeactivate
14241              * Fires before a tab is de-activated - can be used to do validation on a form.
14242              * @param {Roo.bootstrap.TabPanel} this
14243              * @return {Boolean} false if there is an error
14244             
14245          */
14246         'beforedeactivate': true
14247      });
14248     
14249     this.tabId = this.tabId || Roo.id();
14250   
14251 };
14252
14253 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14254     
14255     active: false,
14256     html: false,
14257     tabId: false,
14258     navId : false,
14259     
14260     getAutoCreate : function(){
14261         var cfg = {
14262             tag: 'div',
14263             // item is needed for carousel - not sure if it has any effect otherwise
14264             cls: 'tab-pane item',
14265             html: this.html || ''
14266         };
14267         
14268         if(this.active){
14269             cfg.cls += ' active';
14270         }
14271         
14272         if(this.tabId){
14273             cfg.tabId = this.tabId;
14274         }
14275         
14276         
14277         return cfg;
14278     },
14279     
14280     initEvents:  function()
14281     {
14282         Roo.log('-------- init events on tab panel ---------');
14283         
14284         var p = this.parent();
14285         this.navId = this.navId || p.navId;
14286         
14287         if (typeof(this.navId) != 'undefined') {
14288             // not really needed.. but just in case.. parent should be a NavGroup.
14289             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14290             Roo.log(['register', tg, this]);
14291             tg.register(this);
14292         }
14293     },
14294     
14295     
14296     onRender : function(ct, position)
14297     {
14298        // Roo.log("Call onRender: " + this.xtype);
14299         
14300         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14301         
14302         
14303         
14304         
14305         
14306     },
14307     
14308     setActive: function(state)
14309     {
14310         Roo.log("panel - set active " + this.tabId + "=" + state);
14311         
14312         this.active = state;
14313         if (!state) {
14314             this.el.removeClass('active');
14315             
14316         } else  if (!this.el.hasClass('active')) {
14317             this.el.addClass('active');
14318         }
14319         this.fireEvent('changed', this, state);
14320     }
14321     
14322     
14323 });
14324  
14325
14326  
14327
14328  /*
14329  * - LGPL
14330  *
14331  * DateField
14332  * 
14333  */
14334
14335 /**
14336  * @class Roo.bootstrap.DateField
14337  * @extends Roo.bootstrap.Input
14338  * Bootstrap DateField class
14339  * @cfg {Number} weekStart default 0
14340  * @cfg {Number} weekStart default 0
14341  * @cfg {Number} viewMode default empty, (months|years)
14342  * @cfg {Number} minViewMode default empty, (months|years)
14343  * @cfg {Number} startDate default -Infinity
14344  * @cfg {Number} endDate default Infinity
14345  * @cfg {Boolean} todayHighlight default false
14346  * @cfg {Boolean} todayBtn default false
14347  * @cfg {Boolean} calendarWeeks default false
14348  * @cfg {Object} daysOfWeekDisabled default empty
14349  * 
14350  * @cfg {Boolean} keyboardNavigation default true
14351  * @cfg {String} language default en
14352  * 
14353  * @constructor
14354  * Create a new DateField
14355  * @param {Object} config The config object
14356  */
14357
14358 Roo.bootstrap.DateField = function(config){
14359     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14360      this.addEvents({
14361             /**
14362              * @event show
14363              * Fires when this field show.
14364              * @param {Roo.bootstrap.DateField} this
14365              * @param {Mixed} date The date value
14366              */
14367             show : true,
14368             /**
14369              * @event show
14370              * Fires when this field hide.
14371              * @param {Roo.bootstrap.DateField} this
14372              * @param {Mixed} date The date value
14373              */
14374             hide : true,
14375             /**
14376              * @event select
14377              * Fires when select a date.
14378              * @param {Roo.bootstrap.DateField} this
14379              * @param {Mixed} date The date value
14380              */
14381             select : true
14382         });
14383 };
14384
14385 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14386     
14387     /**
14388      * @cfg {String} format
14389      * The default date format string which can be overriden for localization support.  The format must be
14390      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14391      */
14392     format : "m/d/y",
14393     /**
14394      * @cfg {String} altFormats
14395      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14396      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14397      */
14398     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14399     
14400     weekStart : 0,
14401     
14402     viewMode : '',
14403     
14404     minViewMode : '',
14405     
14406     todayHighlight : false,
14407     
14408     todayBtn: false,
14409     
14410     language: 'en',
14411     
14412     keyboardNavigation: true,
14413     
14414     calendarWeeks: false,
14415     
14416     startDate: -Infinity,
14417     
14418     endDate: Infinity,
14419     
14420     daysOfWeekDisabled: [],
14421     
14422     _events: [],
14423     
14424     UTCDate: function()
14425     {
14426         return new Date(Date.UTC.apply(Date, arguments));
14427     },
14428     
14429     UTCToday: function()
14430     {
14431         var today = new Date();
14432         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14433     },
14434     
14435     getDate: function() {
14436             var d = this.getUTCDate();
14437             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14438     },
14439     
14440     getUTCDate: function() {
14441             return this.date;
14442     },
14443     
14444     setDate: function(d) {
14445             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14446     },
14447     
14448     setUTCDate: function(d) {
14449             this.date = d;
14450             this.setValue(this.formatDate(this.date));
14451     },
14452         
14453     onRender: function(ct, position)
14454     {
14455         
14456         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14457         
14458         this.language = this.language || 'en';
14459         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14460         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14461         
14462         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14463         this.format = this.format || 'm/d/y';
14464         this.isInline = false;
14465         this.isInput = true;
14466         this.component = this.el.select('.add-on', true).first() || false;
14467         this.component = (this.component && this.component.length === 0) ? false : this.component;
14468         this.hasInput = this.component && this.inputEL().length;
14469         
14470         if (typeof(this.minViewMode === 'string')) {
14471             switch (this.minViewMode) {
14472                 case 'months':
14473                     this.minViewMode = 1;
14474                     break;
14475                 case 'years':
14476                     this.minViewMode = 2;
14477                     break;
14478                 default:
14479                     this.minViewMode = 0;
14480                     break;
14481             }
14482         }
14483         
14484         if (typeof(this.viewMode === 'string')) {
14485             switch (this.viewMode) {
14486                 case 'months':
14487                     this.viewMode = 1;
14488                     break;
14489                 case 'years':
14490                     this.viewMode = 2;
14491                     break;
14492                 default:
14493                     this.viewMode = 0;
14494                     break;
14495             }
14496         }
14497                 
14498         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14499         
14500 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14501         
14502         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14503         
14504         this.picker().on('mousedown', this.onMousedown, this);
14505         this.picker().on('click', this.onClick, this);
14506         
14507         this.picker().addClass('datepicker-dropdown');
14508         
14509         this.startViewMode = this.viewMode;
14510         
14511         
14512         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14513             if(!this.calendarWeeks){
14514                 v.remove();
14515                 return;
14516             };
14517             
14518             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14519             v.attr('colspan', function(i, val){
14520                 return parseInt(val) + 1;
14521             });
14522         })
14523                         
14524         
14525         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14526         
14527         this.setStartDate(this.startDate);
14528         this.setEndDate(this.endDate);
14529         
14530         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14531         
14532         this.fillDow();
14533         this.fillMonths();
14534         this.update();
14535         this.showMode();
14536         
14537         if(this.isInline) {
14538             this.show();
14539         }
14540     },
14541     
14542     picker : function()
14543     {
14544         return this.pickerEl;
14545 //        return this.el.select('.datepicker', true).first();
14546     },
14547     
14548     fillDow: function()
14549     {
14550         var dowCnt = this.weekStart;
14551         
14552         var dow = {
14553             tag: 'tr',
14554             cn: [
14555                 
14556             ]
14557         };
14558         
14559         if(this.calendarWeeks){
14560             dow.cn.push({
14561                 tag: 'th',
14562                 cls: 'cw',
14563                 html: '&nbsp;'
14564             })
14565         }
14566         
14567         while (dowCnt < this.weekStart + 7) {
14568             dow.cn.push({
14569                 tag: 'th',
14570                 cls: 'dow',
14571                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14572             });
14573         }
14574         
14575         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14576     },
14577     
14578     fillMonths: function()
14579     {    
14580         var i = 0
14581         var months = this.picker().select('>.datepicker-months td', true).first();
14582         
14583         months.dom.innerHTML = '';
14584         
14585         while (i < 12) {
14586             var month = {
14587                 tag: 'span',
14588                 cls: 'month',
14589                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14590             }
14591             
14592             months.createChild(month);
14593         }
14594         
14595     },
14596     
14597     update: function()
14598     {
14599         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14600         
14601         if (this.date < this.startDate) {
14602             this.viewDate = new Date(this.startDate);
14603         } else if (this.date > this.endDate) {
14604             this.viewDate = new Date(this.endDate);
14605         } else {
14606             this.viewDate = new Date(this.date);
14607         }
14608         
14609         this.fill();
14610     },
14611     
14612     fill: function() 
14613     {
14614         var d = new Date(this.viewDate),
14615                 year = d.getUTCFullYear(),
14616                 month = d.getUTCMonth(),
14617                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14618                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14619                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14620                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14621                 currentDate = this.date && this.date.valueOf(),
14622                 today = this.UTCToday();
14623         
14624         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14625         
14626 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14627         
14628 //        this.picker.select('>tfoot th.today').
14629 //                                              .text(dates[this.language].today)
14630 //                                              .toggle(this.todayBtn !== false);
14631     
14632         this.updateNavArrows();
14633         this.fillMonths();
14634                                                 
14635         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14636         
14637         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14638          
14639         prevMonth.setUTCDate(day);
14640         
14641         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14642         
14643         var nextMonth = new Date(prevMonth);
14644         
14645         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14646         
14647         nextMonth = nextMonth.valueOf();
14648         
14649         var fillMonths = false;
14650         
14651         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14652         
14653         while(prevMonth.valueOf() < nextMonth) {
14654             var clsName = '';
14655             
14656             if (prevMonth.getUTCDay() === this.weekStart) {
14657                 if(fillMonths){
14658                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14659                 }
14660                     
14661                 fillMonths = {
14662                     tag: 'tr',
14663                     cn: []
14664                 };
14665                 
14666                 if(this.calendarWeeks){
14667                     // ISO 8601: First week contains first thursday.
14668                     // ISO also states week starts on Monday, but we can be more abstract here.
14669                     var
14670                     // Start of current week: based on weekstart/current date
14671                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14672                     // Thursday of this week
14673                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14674                     // First Thursday of year, year from thursday
14675                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14676                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14677                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14678                     
14679                     fillMonths.cn.push({
14680                         tag: 'td',
14681                         cls: 'cw',
14682                         html: calWeek
14683                     });
14684                 }
14685             }
14686             
14687             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14688                 clsName += ' old';
14689             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14690                 clsName += ' new';
14691             }
14692             if (this.todayHighlight &&
14693                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14694                 prevMonth.getUTCMonth() == today.getMonth() &&
14695                 prevMonth.getUTCDate() == today.getDate()) {
14696                 clsName += ' today';
14697             }
14698             
14699             if (currentDate && prevMonth.valueOf() === currentDate) {
14700                 clsName += ' active';
14701             }
14702             
14703             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14704                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14705                     clsName += ' disabled';
14706             }
14707             
14708             fillMonths.cn.push({
14709                 tag: 'td',
14710                 cls: 'day ' + clsName,
14711                 html: prevMonth.getDate()
14712             })
14713             
14714             prevMonth.setDate(prevMonth.getDate()+1);
14715         }
14716           
14717         var currentYear = this.date && this.date.getUTCFullYear();
14718         var currentMonth = this.date && this.date.getUTCMonth();
14719         
14720         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14721         
14722         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14723             v.removeClass('active');
14724             
14725             if(currentYear === year && k === currentMonth){
14726                 v.addClass('active');
14727             }
14728             
14729             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14730                 v.addClass('disabled');
14731             }
14732             
14733         });
14734         
14735         
14736         year = parseInt(year/10, 10) * 10;
14737         
14738         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14739         
14740         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14741         
14742         year -= 1;
14743         for (var i = -1; i < 11; i++) {
14744             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14745                 tag: 'span',
14746                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14747                 html: year
14748             })
14749             
14750             year += 1;
14751         }
14752     },
14753     
14754     showMode: function(dir) 
14755     {
14756         if (dir) {
14757             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14758         }
14759         Roo.each(this.picker().select('>div',true).elements, function(v){
14760             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14761             v.hide();
14762         });
14763         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14764     },
14765     
14766     place: function()
14767     {
14768         if(this.isInline) return;
14769         
14770         this.picker().removeClass(['bottom', 'top']);
14771         
14772         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14773             /*
14774              * place to the top of element!
14775              *
14776              */
14777             
14778             this.picker().addClass('top');
14779             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14780             
14781             return;
14782         }
14783         
14784         this.picker().addClass('bottom');
14785         
14786         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14787     },
14788     
14789     parseDate : function(value)
14790     {
14791         if(!value || value instanceof Date){
14792             return value;
14793         }
14794         var v = Date.parseDate(value, this.format);
14795         if (!v && this.useIso) {
14796             v = Date.parseDate(value, 'Y-m-d');
14797         }
14798         if(!v && this.altFormats){
14799             if(!this.altFormatsArray){
14800                 this.altFormatsArray = this.altFormats.split("|");
14801             }
14802             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14803                 v = Date.parseDate(value, this.altFormatsArray[i]);
14804             }
14805         }
14806         return v;
14807     },
14808     
14809     formatDate : function(date, fmt)
14810     {
14811         return (!date || !(date instanceof Date)) ?
14812         date : date.dateFormat(fmt || this.format);
14813     },
14814     
14815     onFocus : function()
14816     {
14817         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14818         this.show();
14819     },
14820     
14821     onBlur : function()
14822     {
14823         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14824         
14825         var d = this.inputEl().getValue();
14826         
14827         this.setValue(d);
14828                 
14829         this.hide();
14830     },
14831     
14832     show : function()
14833     {
14834         this.picker().show();
14835         this.update();
14836         this.place();
14837         
14838         this.fireEvent('show', this, this.date);
14839     },
14840     
14841     hide : function()
14842     {
14843         if(this.isInline) return;
14844         this.picker().hide();
14845         this.viewMode = this.startViewMode;
14846         this.showMode();
14847         
14848         this.fireEvent('hide', this, this.date);
14849         
14850     },
14851     
14852     onMousedown: function(e)
14853     {
14854         e.stopPropagation();
14855         e.preventDefault();
14856     },
14857     
14858     keyup: function(e)
14859     {
14860         Roo.bootstrap.DateField.superclass.keyup.call(this);
14861         this.update();
14862     },
14863
14864     setValue: function(v)
14865     {
14866         var d = new Date(v).clearTime();
14867         
14868         if(isNaN(d.getTime())){
14869             this.date = this.viewDate = '';
14870             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14871             return;
14872         }
14873         
14874         v = this.formatDate(d);
14875         
14876         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14877         
14878         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14879      
14880         this.update();
14881
14882         this.fireEvent('select', this, this.date);
14883         
14884     },
14885     
14886     getValue: function()
14887     {
14888         return this.formatDate(this.date);
14889     },
14890     
14891     fireKey: function(e)
14892     {
14893         if (!this.picker().isVisible()){
14894             if (e.keyCode == 27) // allow escape to hide and re-show picker
14895                 this.show();
14896             return;
14897         }
14898         
14899         var dateChanged = false,
14900         dir, day, month,
14901         newDate, newViewDate;
14902         
14903         switch(e.keyCode){
14904             case 27: // escape
14905                 this.hide();
14906                 e.preventDefault();
14907                 break;
14908             case 37: // left
14909             case 39: // right
14910                 if (!this.keyboardNavigation) break;
14911                 dir = e.keyCode == 37 ? -1 : 1;
14912                 
14913                 if (e.ctrlKey){
14914                     newDate = this.moveYear(this.date, dir);
14915                     newViewDate = this.moveYear(this.viewDate, dir);
14916                 } else if (e.shiftKey){
14917                     newDate = this.moveMonth(this.date, dir);
14918                     newViewDate = this.moveMonth(this.viewDate, dir);
14919                 } else {
14920                     newDate = new Date(this.date);
14921                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14922                     newViewDate = new Date(this.viewDate);
14923                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14924                 }
14925                 if (this.dateWithinRange(newDate)){
14926                     this.date = newDate;
14927                     this.viewDate = newViewDate;
14928                     this.setValue(this.formatDate(this.date));
14929 //                    this.update();
14930                     e.preventDefault();
14931                     dateChanged = true;
14932                 }
14933                 break;
14934             case 38: // up
14935             case 40: // down
14936                 if (!this.keyboardNavigation) break;
14937                 dir = e.keyCode == 38 ? -1 : 1;
14938                 if (e.ctrlKey){
14939                     newDate = this.moveYear(this.date, dir);
14940                     newViewDate = this.moveYear(this.viewDate, dir);
14941                 } else if (e.shiftKey){
14942                     newDate = this.moveMonth(this.date, dir);
14943                     newViewDate = this.moveMonth(this.viewDate, dir);
14944                 } else {
14945                     newDate = new Date(this.date);
14946                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14947                     newViewDate = new Date(this.viewDate);
14948                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14949                 }
14950                 if (this.dateWithinRange(newDate)){
14951                     this.date = newDate;
14952                     this.viewDate = newViewDate;
14953                     this.setValue(this.formatDate(this.date));
14954 //                    this.update();
14955                     e.preventDefault();
14956                     dateChanged = true;
14957                 }
14958                 break;
14959             case 13: // enter
14960                 this.setValue(this.formatDate(this.date));
14961                 this.hide();
14962                 e.preventDefault();
14963                 break;
14964             case 9: // tab
14965                 this.setValue(this.formatDate(this.date));
14966                 this.hide();
14967                 break;
14968             case 16: // shift
14969             case 17: // ctrl
14970             case 18: // alt
14971                 break;
14972             default :
14973                 this.hide();
14974                 
14975         }
14976     },
14977     
14978     
14979     onClick: function(e) 
14980     {
14981         e.stopPropagation();
14982         e.preventDefault();
14983         
14984         var target = e.getTarget();
14985         
14986         if(target.nodeName.toLowerCase() === 'i'){
14987             target = Roo.get(target).dom.parentNode;
14988         }
14989         
14990         var nodeName = target.nodeName;
14991         var className = target.className;
14992         var html = target.innerHTML;
14993         
14994         switch(nodeName.toLowerCase()) {
14995             case 'th':
14996                 switch(className) {
14997                     case 'switch':
14998                         this.showMode(1);
14999                         break;
15000                     case 'prev':
15001                     case 'next':
15002                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15003                         switch(this.viewMode){
15004                                 case 0:
15005                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15006                                         break;
15007                                 case 1:
15008                                 case 2:
15009                                         this.viewDate = this.moveYear(this.viewDate, dir);
15010                                         break;
15011                         }
15012                         this.fill();
15013                         break;
15014                     case 'today':
15015                         var date = new Date();
15016                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15017 //                        this.fill()
15018                         this.setValue(this.formatDate(this.date));
15019                         
15020                         this.hide();
15021                         break;
15022                 }
15023                 break;
15024             case 'span':
15025                 if (className.indexOf('disabled') === -1) {
15026                     this.viewDate.setUTCDate(1);
15027                     if (className.indexOf('month') !== -1) {
15028                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15029                     } else {
15030                         var year = parseInt(html, 10) || 0;
15031                         this.viewDate.setUTCFullYear(year);
15032                         
15033                     }
15034                     this.showMode(-1);
15035                     this.fill();
15036                 }
15037                 break;
15038                 
15039             case 'td':
15040                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15041                     var day = parseInt(html, 10) || 1;
15042                     var year = this.viewDate.getUTCFullYear(),
15043                         month = this.viewDate.getUTCMonth();
15044
15045                     if (className.indexOf('old') !== -1) {
15046                         if(month === 0 ){
15047                             month = 11;
15048                             year -= 1;
15049                         }else{
15050                             month -= 1;
15051                         }
15052                     } else if (className.indexOf('new') !== -1) {
15053                         if (month == 11) {
15054                             month = 0;
15055                             year += 1;
15056                         } else {
15057                             month += 1;
15058                         }
15059                     }
15060                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15061                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15062 //                    this.fill();
15063                     this.setValue(this.formatDate(this.date));
15064                     this.hide();
15065                 }
15066                 break;
15067         }
15068     },
15069     
15070     setStartDate: function(startDate)
15071     {
15072         this.startDate = startDate || -Infinity;
15073         if (this.startDate !== -Infinity) {
15074             this.startDate = this.parseDate(this.startDate);
15075         }
15076         this.update();
15077         this.updateNavArrows();
15078     },
15079
15080     setEndDate: function(endDate)
15081     {
15082         this.endDate = endDate || Infinity;
15083         if (this.endDate !== Infinity) {
15084             this.endDate = this.parseDate(this.endDate);
15085         }
15086         this.update();
15087         this.updateNavArrows();
15088     },
15089     
15090     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15091     {
15092         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15093         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15094             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15095         }
15096         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15097             return parseInt(d, 10);
15098         });
15099         this.update();
15100         this.updateNavArrows();
15101     },
15102     
15103     updateNavArrows: function() 
15104     {
15105         var d = new Date(this.viewDate),
15106         year = d.getUTCFullYear(),
15107         month = d.getUTCMonth();
15108         
15109         Roo.each(this.picker().select('.prev', true).elements, function(v){
15110             v.show();
15111             switch (this.viewMode) {
15112                 case 0:
15113
15114                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15115                         v.hide();
15116                     }
15117                     break;
15118                 case 1:
15119                 case 2:
15120                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15121                         v.hide();
15122                     }
15123                     break;
15124             }
15125         });
15126         
15127         Roo.each(this.picker().select('.next', true).elements, function(v){
15128             v.show();
15129             switch (this.viewMode) {
15130                 case 0:
15131
15132                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15133                         v.hide();
15134                     }
15135                     break;
15136                 case 1:
15137                 case 2:
15138                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15139                         v.hide();
15140                     }
15141                     break;
15142             }
15143         })
15144     },
15145     
15146     moveMonth: function(date, dir)
15147     {
15148         if (!dir) return date;
15149         var new_date = new Date(date.valueOf()),
15150         day = new_date.getUTCDate(),
15151         month = new_date.getUTCMonth(),
15152         mag = Math.abs(dir),
15153         new_month, test;
15154         dir = dir > 0 ? 1 : -1;
15155         if (mag == 1){
15156             test = dir == -1
15157             // If going back one month, make sure month is not current month
15158             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15159             ? function(){
15160                 return new_date.getUTCMonth() == month;
15161             }
15162             // If going forward one month, make sure month is as expected
15163             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15164             : function(){
15165                 return new_date.getUTCMonth() != new_month;
15166             };
15167             new_month = month + dir;
15168             new_date.setUTCMonth(new_month);
15169             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15170             if (new_month < 0 || new_month > 11)
15171                 new_month = (new_month + 12) % 12;
15172         } else {
15173             // For magnitudes >1, move one month at a time...
15174             for (var i=0; i<mag; i++)
15175                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15176                 new_date = this.moveMonth(new_date, dir);
15177             // ...then reset the day, keeping it in the new month
15178             new_month = new_date.getUTCMonth();
15179             new_date.setUTCDate(day);
15180             test = function(){
15181                 return new_month != new_date.getUTCMonth();
15182             };
15183         }
15184         // Common date-resetting loop -- if date is beyond end of month, make it
15185         // end of month
15186         while (test()){
15187             new_date.setUTCDate(--day);
15188             new_date.setUTCMonth(new_month);
15189         }
15190         return new_date;
15191     },
15192
15193     moveYear: function(date, dir)
15194     {
15195         return this.moveMonth(date, dir*12);
15196     },
15197
15198     dateWithinRange: function(date)
15199     {
15200         return date >= this.startDate && date <= this.endDate;
15201     },
15202
15203     
15204     remove: function() 
15205     {
15206         this.picker().remove();
15207     }
15208    
15209 });
15210
15211 Roo.apply(Roo.bootstrap.DateField,  {
15212     
15213     head : {
15214         tag: 'thead',
15215         cn: [
15216         {
15217             tag: 'tr',
15218             cn: [
15219             {
15220                 tag: 'th',
15221                 cls: 'prev',
15222                 html: '<i class="fa fa-arrow-left"/>'
15223             },
15224             {
15225                 tag: 'th',
15226                 cls: 'switch',
15227                 colspan: '5'
15228             },
15229             {
15230                 tag: 'th',
15231                 cls: 'next',
15232                 html: '<i class="fa fa-arrow-right"/>'
15233             }
15234
15235             ]
15236         }
15237         ]
15238     },
15239     
15240     content : {
15241         tag: 'tbody',
15242         cn: [
15243         {
15244             tag: 'tr',
15245             cn: [
15246             {
15247                 tag: 'td',
15248                 colspan: '7'
15249             }
15250             ]
15251         }
15252         ]
15253     },
15254     
15255     footer : {
15256         tag: 'tfoot',
15257         cn: [
15258         {
15259             tag: 'tr',
15260             cn: [
15261             {
15262                 tag: 'th',
15263                 colspan: '7',
15264                 cls: 'today'
15265             }
15266                     
15267             ]
15268         }
15269         ]
15270     },
15271     
15272     dates:{
15273         en: {
15274             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15275             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15276             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15277             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15278             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15279             today: "Today"
15280         }
15281     },
15282     
15283     modes: [
15284     {
15285         clsName: 'days',
15286         navFnc: 'Month',
15287         navStep: 1
15288     },
15289     {
15290         clsName: 'months',
15291         navFnc: 'FullYear',
15292         navStep: 1
15293     },
15294     {
15295         clsName: 'years',
15296         navFnc: 'FullYear',
15297         navStep: 10
15298     }]
15299 });
15300
15301 Roo.apply(Roo.bootstrap.DateField,  {
15302   
15303     template : {
15304         tag: 'div',
15305         cls: 'datepicker dropdown-menu',
15306         cn: [
15307         {
15308             tag: 'div',
15309             cls: 'datepicker-days',
15310             cn: [
15311             {
15312                 tag: 'table',
15313                 cls: 'table-condensed',
15314                 cn:[
15315                 Roo.bootstrap.DateField.head,
15316                 {
15317                     tag: 'tbody'
15318                 },
15319                 Roo.bootstrap.DateField.footer
15320                 ]
15321             }
15322             ]
15323         },
15324         {
15325             tag: 'div',
15326             cls: 'datepicker-months',
15327             cn: [
15328             {
15329                 tag: 'table',
15330                 cls: 'table-condensed',
15331                 cn:[
15332                 Roo.bootstrap.DateField.head,
15333                 Roo.bootstrap.DateField.content,
15334                 Roo.bootstrap.DateField.footer
15335                 ]
15336             }
15337             ]
15338         },
15339         {
15340             tag: 'div',
15341             cls: 'datepicker-years',
15342             cn: [
15343             {
15344                 tag: 'table',
15345                 cls: 'table-condensed',
15346                 cn:[
15347                 Roo.bootstrap.DateField.head,
15348                 Roo.bootstrap.DateField.content,
15349                 Roo.bootstrap.DateField.footer
15350                 ]
15351             }
15352             ]
15353         }
15354         ]
15355     }
15356 });
15357
15358  
15359
15360  /*
15361  * - LGPL
15362  *
15363  * TimeField
15364  * 
15365  */
15366
15367 /**
15368  * @class Roo.bootstrap.TimeField
15369  * @extends Roo.bootstrap.Input
15370  * Bootstrap DateField class
15371  * 
15372  * 
15373  * @constructor
15374  * Create a new TimeField
15375  * @param {Object} config The config object
15376  */
15377
15378 Roo.bootstrap.TimeField = function(config){
15379     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15380     this.addEvents({
15381             /**
15382              * @event show
15383              * Fires when this field show.
15384              * @param {Roo.bootstrap.DateField} this
15385              * @param {Mixed} date The date value
15386              */
15387             show : true,
15388             /**
15389              * @event show
15390              * Fires when this field hide.
15391              * @param {Roo.bootstrap.DateField} this
15392              * @param {Mixed} date The date value
15393              */
15394             hide : true,
15395             /**
15396              * @event select
15397              * Fires when select a date.
15398              * @param {Roo.bootstrap.DateField} this
15399              * @param {Mixed} date The date value
15400              */
15401             select : true
15402         });
15403 };
15404
15405 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15406     
15407     /**
15408      * @cfg {String} format
15409      * The default time format string which can be overriden for localization support.  The format must be
15410      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15411      */
15412     format : "H:i",
15413        
15414     onRender: function(ct, position)
15415     {
15416         
15417         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15418                 
15419         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15420         
15421         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15422         
15423         this.pop = this.picker().select('>.datepicker-time',true).first();
15424         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15425         
15426         this.picker().on('mousedown', this.onMousedown, this);
15427         this.picker().on('click', this.onClick, this);
15428         
15429         this.picker().addClass('datepicker-dropdown');
15430     
15431         this.fillTime();
15432         this.update();
15433             
15434         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15435         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15436         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15437         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15438         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15439         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15440
15441     },
15442     
15443     fireKey: function(e){
15444         if (!this.picker().isVisible()){
15445             if (e.keyCode == 27) // allow escape to hide and re-show picker
15446                 this.show();
15447             return;
15448         }
15449
15450         e.preventDefault();
15451         
15452         switch(e.keyCode){
15453             case 27: // escape
15454                 this.hide();
15455                 break;
15456             case 37: // left
15457             case 39: // right
15458                 this.onTogglePeriod();
15459                 break;
15460             case 38: // up
15461                 this.onIncrementMinutes();
15462                 break;
15463             case 40: // down
15464                 this.onDecrementMinutes();
15465                 break;
15466             case 13: // enter
15467             case 9: // tab
15468                 this.setTime();
15469                 break;
15470         }
15471     },
15472     
15473     onClick: function(e) {
15474         e.stopPropagation();
15475         e.preventDefault();
15476     },
15477     
15478     picker : function()
15479     {
15480         return this.el.select('.datepicker', true).first();
15481     },
15482     
15483     fillTime: function()
15484     {    
15485         var time = this.pop.select('tbody', true).first();
15486         
15487         time.dom.innerHTML = '';
15488         
15489         time.createChild({
15490             tag: 'tr',
15491             cn: [
15492                 {
15493                     tag: 'td',
15494                     cn: [
15495                         {
15496                             tag: 'a',
15497                             href: '#',
15498                             cls: 'btn',
15499                             cn: [
15500                                 {
15501                                     tag: 'span',
15502                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15503                                 }
15504                             ]
15505                         } 
15506                     ]
15507                 },
15508                 {
15509                     tag: 'td',
15510                     cls: 'separator'
15511                 },
15512                 {
15513                     tag: 'td',
15514                     cn: [
15515                         {
15516                             tag: 'a',
15517                             href: '#',
15518                             cls: 'btn',
15519                             cn: [
15520                                 {
15521                                     tag: 'span',
15522                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15523                                 }
15524                             ]
15525                         }
15526                     ]
15527                 },
15528                 {
15529                     tag: 'td',
15530                     cls: 'separator'
15531                 }
15532             ]
15533         });
15534         
15535         time.createChild({
15536             tag: 'tr',
15537             cn: [
15538                 {
15539                     tag: 'td',
15540                     cn: [
15541                         {
15542                             tag: 'span',
15543                             cls: 'timepicker-hour',
15544                             html: '00'
15545                         }  
15546                     ]
15547                 },
15548                 {
15549                     tag: 'td',
15550                     cls: 'separator',
15551                     html: ':'
15552                 },
15553                 {
15554                     tag: 'td',
15555                     cn: [
15556                         {
15557                             tag: 'span',
15558                             cls: 'timepicker-minute',
15559                             html: '00'
15560                         }  
15561                     ]
15562                 },
15563                 {
15564                     tag: 'td',
15565                     cls: 'separator'
15566                 },
15567                 {
15568                     tag: 'td',
15569                     cn: [
15570                         {
15571                             tag: 'button',
15572                             type: 'button',
15573                             cls: 'btn btn-primary period',
15574                             html: 'AM'
15575                             
15576                         }
15577                     ]
15578                 }
15579             ]
15580         });
15581         
15582         time.createChild({
15583             tag: 'tr',
15584             cn: [
15585                 {
15586                     tag: 'td',
15587                     cn: [
15588                         {
15589                             tag: 'a',
15590                             href: '#',
15591                             cls: 'btn',
15592                             cn: [
15593                                 {
15594                                     tag: 'span',
15595                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15596                                 }
15597                             ]
15598                         }
15599                     ]
15600                 },
15601                 {
15602                     tag: 'td',
15603                     cls: 'separator'
15604                 },
15605                 {
15606                     tag: 'td',
15607                     cn: [
15608                         {
15609                             tag: 'a',
15610                             href: '#',
15611                             cls: 'btn',
15612                             cn: [
15613                                 {
15614                                     tag: 'span',
15615                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15616                                 }
15617                             ]
15618                         }
15619                     ]
15620                 },
15621                 {
15622                     tag: 'td',
15623                     cls: 'separator'
15624                 }
15625             ]
15626         });
15627         
15628     },
15629     
15630     update: function()
15631     {
15632         
15633         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15634         
15635         this.fill();
15636     },
15637     
15638     fill: function() 
15639     {
15640         var hours = this.time.getHours();
15641         var minutes = this.time.getMinutes();
15642         var period = 'AM';
15643         
15644         if(hours > 11){
15645             period = 'PM';
15646         }
15647         
15648         if(hours == 0){
15649             hours = 12;
15650         }
15651         
15652         
15653         if(hours > 12){
15654             hours = hours - 12;
15655         }
15656         
15657         if(hours < 10){
15658             hours = '0' + hours;
15659         }
15660         
15661         if(minutes < 10){
15662             minutes = '0' + minutes;
15663         }
15664         
15665         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15666         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15667         this.pop.select('button', true).first().dom.innerHTML = period;
15668         
15669     },
15670     
15671     place: function()
15672     {   
15673         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15674         
15675         var cls = ['bottom'];
15676         
15677         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15678             cls.pop();
15679             cls.push('top');
15680         }
15681         
15682         cls.push('right');
15683         
15684         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15685             cls.pop();
15686             cls.push('left');
15687         }
15688         
15689         this.picker().addClass(cls.join('-'));
15690         
15691         var _this = this;
15692         
15693         Roo.each(cls, function(c){
15694             if(c == 'bottom'){
15695                 _this.picker().setTop(_this.inputEl().getHeight());
15696                 return;
15697             }
15698             if(c == 'top'){
15699                 _this.picker().setTop(0 - _this.picker().getHeight());
15700                 return;
15701             }
15702             
15703             if(c == 'left'){
15704                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15705                 return;
15706             }
15707             if(c == 'right'){
15708                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15709                 return;
15710             }
15711         });
15712         
15713     },
15714   
15715     onFocus : function()
15716     {
15717         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15718         this.show();
15719     },
15720     
15721     onBlur : function()
15722     {
15723         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15724         this.hide();
15725     },
15726     
15727     show : function()
15728     {
15729         this.picker().show();
15730         this.pop.show();
15731         this.update();
15732         this.place();
15733         
15734         this.fireEvent('show', this, this.date);
15735     },
15736     
15737     hide : function()
15738     {
15739         this.picker().hide();
15740         this.pop.hide();
15741         
15742         this.fireEvent('hide', this, this.date);
15743     },
15744     
15745     setTime : function()
15746     {
15747         this.hide();
15748         this.setValue(this.time.format(this.format));
15749         
15750         this.fireEvent('select', this, this.date);
15751         
15752         
15753     },
15754     
15755     onMousedown: function(e){
15756         e.stopPropagation();
15757         e.preventDefault();
15758     },
15759     
15760     onIncrementHours: function()
15761     {
15762         Roo.log('onIncrementHours');
15763         this.time = this.time.add(Date.HOUR, 1);
15764         this.update();
15765         
15766     },
15767     
15768     onDecrementHours: function()
15769     {
15770         Roo.log('onDecrementHours');
15771         this.time = this.time.add(Date.HOUR, -1);
15772         this.update();
15773     },
15774     
15775     onIncrementMinutes: function()
15776     {
15777         Roo.log('onIncrementMinutes');
15778         this.time = this.time.add(Date.MINUTE, 1);
15779         this.update();
15780     },
15781     
15782     onDecrementMinutes: function()
15783     {
15784         Roo.log('onDecrementMinutes');
15785         this.time = this.time.add(Date.MINUTE, -1);
15786         this.update();
15787     },
15788     
15789     onTogglePeriod: function()
15790     {
15791         Roo.log('onTogglePeriod');
15792         this.time = this.time.add(Date.HOUR, 12);
15793         this.update();
15794     }
15795     
15796    
15797 });
15798
15799 Roo.apply(Roo.bootstrap.TimeField,  {
15800     
15801     content : {
15802         tag: 'tbody',
15803         cn: [
15804             {
15805                 tag: 'tr',
15806                 cn: [
15807                 {
15808                     tag: 'td',
15809                     colspan: '7'
15810                 }
15811                 ]
15812             }
15813         ]
15814     },
15815     
15816     footer : {
15817         tag: 'tfoot',
15818         cn: [
15819             {
15820                 tag: 'tr',
15821                 cn: [
15822                 {
15823                     tag: 'th',
15824                     colspan: '7',
15825                     cls: '',
15826                     cn: [
15827                         {
15828                             tag: 'button',
15829                             cls: 'btn btn-info ok',
15830                             html: 'OK'
15831                         }
15832                     ]
15833                 }
15834
15835                 ]
15836             }
15837         ]
15838     }
15839 });
15840
15841 Roo.apply(Roo.bootstrap.TimeField,  {
15842   
15843     template : {
15844         tag: 'div',
15845         cls: 'datepicker dropdown-menu',
15846         cn: [
15847             {
15848                 tag: 'div',
15849                 cls: 'datepicker-time',
15850                 cn: [
15851                 {
15852                     tag: 'table',
15853                     cls: 'table-condensed',
15854                     cn:[
15855                     Roo.bootstrap.TimeField.content,
15856                     Roo.bootstrap.TimeField.footer
15857                     ]
15858                 }
15859                 ]
15860             }
15861         ]
15862     }
15863 });
15864
15865  
15866
15867  /*
15868  * - LGPL
15869  *
15870  * CheckBox
15871  * 
15872  */
15873
15874 /**
15875  * @class Roo.bootstrap.CheckBox
15876  * @extends Roo.bootstrap.Input
15877  * Bootstrap CheckBox class
15878  * 
15879  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15880  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15881  * @cfg {String} boxLabel The text that appears beside the checkbox
15882  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15883  * @cfg {Boolean} checked initnal the element
15884  * 
15885  * 
15886  * @constructor
15887  * Create a new CheckBox
15888  * @param {Object} config The config object
15889  */
15890
15891 Roo.bootstrap.CheckBox = function(config){
15892     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15893    
15894         this.addEvents({
15895             /**
15896             * @event check
15897             * Fires when the element is checked or unchecked.
15898             * @param {Roo.bootstrap.CheckBox} this This input
15899             * @param {Boolean} checked The new checked value
15900             */
15901            check : true
15902         });
15903 };
15904
15905 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15906     
15907     inputType: 'checkbox',
15908     inputValue: 1,
15909     valueOff: 0,
15910     boxLabel: false,
15911     checked: false,
15912     weight : false,
15913     
15914     getAutoCreate : function()
15915     {
15916         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15917         
15918         var id = Roo.id();
15919         
15920         var cfg = {};
15921         
15922         cfg.cls = 'form-group checkbox' //input-group
15923         
15924         
15925         
15926         
15927         var input =  {
15928             tag: 'input',
15929             id : id,
15930             type : this.inputType,
15931             value : (!this.checked) ? this.valueOff : this.inputValue,
15932             cls : 'roo-checkbox', //'form-box',
15933             placeholder : this.placeholder || ''
15934             
15935         };
15936         
15937         if (this.weight) { // Validity check?
15938             cfg.cls += " checkbox-" + this.weight;
15939         }
15940         
15941         if (this.disabled) {
15942             input.disabled=true;
15943         }
15944         
15945         if(this.checked){
15946             input.checked = this.checked;
15947         }
15948         
15949         if (this.name) {
15950             input.name = this.name;
15951         }
15952         
15953         if (this.size) {
15954             input.cls += ' input-' + this.size;
15955         }
15956         
15957         var settings=this;
15958         ['xs','sm','md','lg'].map(function(size){
15959             if (settings[size]) {
15960                 cfg.cls += ' col-' + size + '-' + settings[size];
15961             }
15962         });
15963         
15964        
15965         
15966         var inputblock = input;
15967         
15968         
15969         
15970         
15971         if (this.before || this.after) {
15972             
15973             inputblock = {
15974                 cls : 'input-group',
15975                 cn :  [] 
15976             };
15977             if (this.before) {
15978                 inputblock.cn.push({
15979                     tag :'span',
15980                     cls : 'input-group-addon',
15981                     html : this.before
15982                 });
15983             }
15984             inputblock.cn.push(input);
15985             if (this.after) {
15986                 inputblock.cn.push({
15987                     tag :'span',
15988                     cls : 'input-group-addon',
15989                     html : this.after
15990                 });
15991             }
15992             
15993         };
15994         
15995         if (align ==='left' && this.fieldLabel.length) {
15996                 Roo.log("left and has label");
15997                 cfg.cn = [
15998                     
15999                     {
16000                         tag: 'label',
16001                         'for' :  id,
16002                         cls : 'control-label col-md-' + this.labelWidth,
16003                         html : this.fieldLabel
16004                         
16005                     },
16006                     {
16007                         cls : "col-md-" + (12 - this.labelWidth), 
16008                         cn: [
16009                             inputblock
16010                         ]
16011                     }
16012                     
16013                 ];
16014         } else if ( this.fieldLabel.length) {
16015                 Roo.log(" label");
16016                 cfg.cn = [
16017                    
16018                     {
16019                         tag: this.boxLabel ? 'span' : 'label',
16020                         'for': id,
16021                         cls: 'control-label box-input-label',
16022                         //cls : 'input-group-addon',
16023                         html : this.fieldLabel
16024                         
16025                     },
16026                     
16027                     inputblock
16028                     
16029                 ];
16030
16031         } else {
16032             
16033                 Roo.log(" no label && no align");
16034                 cfg.cn = [  inputblock ] ;
16035                 
16036                 
16037         };
16038          if(this.boxLabel){
16039             cfg.cn.push( {
16040                 tag: 'label',
16041                 'for': id,
16042                 cls: 'box-label',
16043                 html: this.boxLabel
16044                 
16045             });
16046         }
16047         
16048         
16049        
16050         return cfg;
16051         
16052     },
16053     
16054     /**
16055      * return the real input element.
16056      */
16057     inputEl: function ()
16058     {
16059         return this.el.select('input.roo-checkbox',true).first();
16060     },
16061     
16062     label: function()
16063     {
16064         return this.el.select('label.control-label',true).first();
16065     },
16066     
16067     initEvents : function()
16068     {
16069 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16070         
16071         this.inputEl().on('click', this.onClick,  this);
16072         
16073     },
16074     
16075     onClick : function()
16076     {   
16077         this.setChecked(!this.checked);
16078     },
16079     
16080     setChecked : function(state,suppressEvent)
16081     {
16082         this.checked = state;
16083         
16084         this.inputEl().dom.checked = state;
16085         
16086         if(suppressEvent !== true){
16087             this.fireEvent('check', this, state);
16088         }
16089         
16090         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16091         
16092     },
16093     
16094     setValue : function(v,suppressEvent)
16095     {
16096         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16097     }
16098     
16099 });
16100
16101  
16102 /*
16103  * - LGPL
16104  *
16105  * Radio
16106  * 
16107  */
16108
16109 /**
16110  * @class Roo.bootstrap.Radio
16111  * @extends Roo.bootstrap.CheckBox
16112  * Bootstrap Radio class
16113
16114  * @constructor
16115  * Create a new Radio
16116  * @param {Object} config The config object
16117  */
16118
16119 Roo.bootstrap.Radio = function(config){
16120     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16121    
16122 };
16123
16124 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16125     
16126     inputType: 'radio',
16127     inputValue: '',
16128     valueOff: '',
16129     
16130     getAutoCreate : function()
16131     {
16132         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16133         
16134         var id = Roo.id();
16135         
16136         var cfg = {};
16137         
16138         cfg.cls = 'form-group radio' //input-group
16139         
16140         var input =  {
16141             tag: 'input',
16142             id : id,
16143             type : this.inputType,
16144             value : (!this.checked) ? this.valueOff : this.inputValue,
16145             cls : 'roo-radio',
16146             placeholder : this.placeholder || ''
16147             
16148         };
16149           if (this.weight) { // Validity check?
16150             cfg.cls += " radio-" + this.weight;
16151         }
16152         if (this.disabled) {
16153             input.disabled=true;
16154         }
16155         
16156         if(this.checked){
16157             input.checked = this.checked;
16158         }
16159         
16160         if (this.name) {
16161             input.name = this.name;
16162         }
16163         
16164         if (this.size) {
16165             input.cls += ' input-' + this.size;
16166         }
16167         
16168         var settings=this;
16169         ['xs','sm','md','lg'].map(function(size){
16170             if (settings[size]) {
16171                 cfg.cls += ' col-' + size + '-' + settings[size];
16172             }
16173         });
16174         
16175         var inputblock = input;
16176         
16177         if (this.before || this.after) {
16178             
16179             inputblock = {
16180                 cls : 'input-group',
16181                 cn :  [] 
16182             };
16183             if (this.before) {
16184                 inputblock.cn.push({
16185                     tag :'span',
16186                     cls : 'input-group-addon',
16187                     html : this.before
16188                 });
16189             }
16190             inputblock.cn.push(input);
16191             if (this.after) {
16192                 inputblock.cn.push({
16193                     tag :'span',
16194                     cls : 'input-group-addon',
16195                     html : this.after
16196                 });
16197             }
16198             
16199         };
16200         
16201         if (align ==='left' && this.fieldLabel.length) {
16202                 Roo.log("left and has label");
16203                 cfg.cn = [
16204                     
16205                     {
16206                         tag: 'label',
16207                         'for' :  id,
16208                         cls : 'control-label col-md-' + this.labelWidth,
16209                         html : this.fieldLabel
16210                         
16211                     },
16212                     {
16213                         cls : "col-md-" + (12 - this.labelWidth), 
16214                         cn: [
16215                             inputblock
16216                         ]
16217                     }
16218                     
16219                 ];
16220         } else if ( this.fieldLabel.length) {
16221                 Roo.log(" label");
16222                  cfg.cn = [
16223                    
16224                     {
16225                         tag: 'label',
16226                         'for': id,
16227                         cls: 'control-label box-input-label',
16228                         //cls : 'input-group-addon',
16229                         html : this.fieldLabel
16230                         
16231                     },
16232                     
16233                     inputblock
16234                     
16235                 ];
16236
16237         } else {
16238             
16239                    Roo.log(" no label && no align");
16240                 cfg.cn = [
16241                     
16242                         inputblock
16243                     
16244                 ];
16245                 
16246                 
16247         };
16248         
16249         if(this.boxLabel){
16250             cfg.cn.push({
16251                 tag: 'label',
16252                 'for': id,
16253                 cls: 'box-label',
16254                 html: this.boxLabel
16255             })
16256         }
16257         
16258         return cfg;
16259         
16260     },
16261     inputEl: function ()
16262     {
16263         return this.el.select('input.roo-radio',true).first();
16264     },
16265     onClick : function()
16266     {   
16267         this.setChecked(true);
16268     },
16269     
16270     setChecked : function(state,suppressEvent)
16271     {
16272         if(state){
16273             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16274                 v.dom.checked = false;
16275             });
16276         }
16277         
16278         this.checked = state;
16279         this.inputEl().dom.checked = state;
16280         
16281         if(suppressEvent !== true){
16282             this.fireEvent('check', this, state);
16283         }
16284         
16285         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16286         
16287     },
16288     
16289     getGroupValue : function()
16290     {
16291         var value = ''
16292         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16293             if(v.dom.checked == true){
16294                 value = v.dom.value;
16295             }
16296         });
16297         
16298         return value;
16299     },
16300     
16301     /**
16302      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16303      * @return {Mixed} value The field value
16304      */
16305     getValue : function(){
16306         return this.getGroupValue();
16307     }
16308     
16309 });
16310
16311  
16312 //<script type="text/javascript">
16313
16314 /*
16315  * Based  Ext JS Library 1.1.1
16316  * Copyright(c) 2006-2007, Ext JS, LLC.
16317  * LGPL
16318  *
16319  */
16320  
16321 /**
16322  * @class Roo.HtmlEditorCore
16323  * @extends Roo.Component
16324  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16325  *
16326  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16327  */
16328
16329 Roo.HtmlEditorCore = function(config){
16330     
16331     
16332     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16333     
16334     
16335     this.addEvents({
16336         /**
16337          * @event initialize
16338          * Fires when the editor is fully initialized (including the iframe)
16339          * @param {Roo.HtmlEditorCore} this
16340          */
16341         initialize: true,
16342         /**
16343          * @event activate
16344          * Fires when the editor is first receives the focus. Any insertion must wait
16345          * until after this event.
16346          * @param {Roo.HtmlEditorCore} this
16347          */
16348         activate: true,
16349          /**
16350          * @event beforesync
16351          * Fires before the textarea is updated with content from the editor iframe. Return false
16352          * to cancel the sync.
16353          * @param {Roo.HtmlEditorCore} this
16354          * @param {String} html
16355          */
16356         beforesync: true,
16357          /**
16358          * @event beforepush
16359          * Fires before the iframe editor is updated with content from the textarea. Return false
16360          * to cancel the push.
16361          * @param {Roo.HtmlEditorCore} this
16362          * @param {String} html
16363          */
16364         beforepush: true,
16365          /**
16366          * @event sync
16367          * Fires when the textarea is updated with content from the editor iframe.
16368          * @param {Roo.HtmlEditorCore} this
16369          * @param {String} html
16370          */
16371         sync: true,
16372          /**
16373          * @event push
16374          * Fires when the iframe editor is updated with content from the textarea.
16375          * @param {Roo.HtmlEditorCore} this
16376          * @param {String} html
16377          */
16378         push: true,
16379         
16380         /**
16381          * @event editorevent
16382          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16383          * @param {Roo.HtmlEditorCore} this
16384          */
16385         editorevent: true
16386     });
16387     
16388     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16389     
16390     // defaults : white / black...
16391     this.applyBlacklists();
16392     
16393     
16394     
16395 };
16396
16397
16398 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16399
16400
16401      /**
16402      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16403      */
16404     
16405     owner : false,
16406     
16407      /**
16408      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16409      *                        Roo.resizable.
16410      */
16411     resizable : false,
16412      /**
16413      * @cfg {Number} height (in pixels)
16414      */   
16415     height: 300,
16416    /**
16417      * @cfg {Number} width (in pixels)
16418      */   
16419     width: 500,
16420     
16421     /**
16422      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16423      * 
16424      */
16425     stylesheets: false,
16426     
16427     // id of frame..
16428     frameId: false,
16429     
16430     // private properties
16431     validationEvent : false,
16432     deferHeight: true,
16433     initialized : false,
16434     activated : false,
16435     sourceEditMode : false,
16436     onFocus : Roo.emptyFn,
16437     iframePad:3,
16438     hideMode:'offsets',
16439     
16440     clearUp: true,
16441     
16442     // blacklist + whitelisted elements..
16443     black: false,
16444     white: false,
16445      
16446     
16447
16448     /**
16449      * Protected method that will not generally be called directly. It
16450      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16451      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16452      */
16453     getDocMarkup : function(){
16454         // body styles..
16455         var st = '';
16456         Roo.log(this.stylesheets);
16457         
16458         // inherit styels from page...?? 
16459         if (this.stylesheets === false) {
16460             
16461             Roo.get(document.head).select('style').each(function(node) {
16462                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16463             });
16464             
16465             Roo.get(document.head).select('link').each(function(node) { 
16466                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16467             });
16468             
16469         } else if (!this.stylesheets.length) {
16470                 // simple..
16471                 st = '<style type="text/css">' +
16472                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16473                    '</style>';
16474         } else {
16475             Roo.each(this.stylesheets, function(s) {
16476                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16477             });
16478             
16479         }
16480         
16481         st +=  '<style type="text/css">' +
16482             'IMG { cursor: pointer } ' +
16483         '</style>';
16484
16485         
16486         return '<html><head>' + st  +
16487             //<style type="text/css">' +
16488             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16489             //'</style>' +
16490             ' </head><body class="roo-htmleditor-body"></body></html>';
16491     },
16492
16493     // private
16494     onRender : function(ct, position)
16495     {
16496         var _t = this;
16497         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16498         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16499         
16500         
16501         this.el.dom.style.border = '0 none';
16502         this.el.dom.setAttribute('tabIndex', -1);
16503         this.el.addClass('x-hidden hide');
16504         
16505         
16506         
16507         if(Roo.isIE){ // fix IE 1px bogus margin
16508             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16509         }
16510        
16511         
16512         this.frameId = Roo.id();
16513         
16514          
16515         
16516         var iframe = this.owner.wrap.createChild({
16517             tag: 'iframe',
16518             cls: 'form-control', // bootstrap..
16519             id: this.frameId,
16520             name: this.frameId,
16521             frameBorder : 'no',
16522             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16523         }, this.el
16524         );
16525         
16526         
16527         this.iframe = iframe.dom;
16528
16529          this.assignDocWin();
16530         
16531         this.doc.designMode = 'on';
16532        
16533         this.doc.open();
16534         this.doc.write(this.getDocMarkup());
16535         this.doc.close();
16536
16537         
16538         var task = { // must defer to wait for browser to be ready
16539             run : function(){
16540                 //console.log("run task?" + this.doc.readyState);
16541                 this.assignDocWin();
16542                 if(this.doc.body || this.doc.readyState == 'complete'){
16543                     try {
16544                         this.doc.designMode="on";
16545                     } catch (e) {
16546                         return;
16547                     }
16548                     Roo.TaskMgr.stop(task);
16549                     this.initEditor.defer(10, this);
16550                 }
16551             },
16552             interval : 10,
16553             duration: 10000,
16554             scope: this
16555         };
16556         Roo.TaskMgr.start(task);
16557
16558         
16559          
16560     },
16561
16562     // private
16563     onResize : function(w, h)
16564     {
16565          Roo.log('resize: ' +w + ',' + h );
16566         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16567         if(!this.iframe){
16568             return;
16569         }
16570         if(typeof w == 'number'){
16571             
16572             this.iframe.style.width = w + 'px';
16573         }
16574         if(typeof h == 'number'){
16575             
16576             this.iframe.style.height = h + 'px';
16577             if(this.doc){
16578                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16579             }
16580         }
16581         
16582     },
16583
16584     /**
16585      * Toggles the editor between standard and source edit mode.
16586      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16587      */
16588     toggleSourceEdit : function(sourceEditMode){
16589         
16590         this.sourceEditMode = sourceEditMode === true;
16591         
16592         if(this.sourceEditMode){
16593  
16594             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16595             
16596         }else{
16597             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16598             //this.iframe.className = '';
16599             this.deferFocus();
16600         }
16601         //this.setSize(this.owner.wrap.getSize());
16602         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16603     },
16604
16605     
16606   
16607
16608     /**
16609      * Protected method that will not generally be called directly. If you need/want
16610      * custom HTML cleanup, this is the method you should override.
16611      * @param {String} html The HTML to be cleaned
16612      * return {String} The cleaned HTML
16613      */
16614     cleanHtml : function(html){
16615         html = String(html);
16616         if(html.length > 5){
16617             if(Roo.isSafari){ // strip safari nonsense
16618                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16619             }
16620         }
16621         if(html == '&nbsp;'){
16622             html = '';
16623         }
16624         return html;
16625     },
16626
16627     /**
16628      * HTML Editor -> Textarea
16629      * Protected method that will not generally be called directly. Syncs the contents
16630      * of the editor iframe with the textarea.
16631      */
16632     syncValue : function(){
16633         if(this.initialized){
16634             var bd = (this.doc.body || this.doc.documentElement);
16635             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16636             var html = bd.innerHTML;
16637             if(Roo.isSafari){
16638                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16639                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16640                 if(m && m[1]){
16641                     html = '<div style="'+m[0]+'">' + html + '</div>';
16642                 }
16643             }
16644             html = this.cleanHtml(html);
16645             // fix up the special chars.. normaly like back quotes in word...
16646             // however we do not want to do this with chinese..
16647             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16648                 var cc = b.charCodeAt();
16649                 if (
16650                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16651                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16652                     (cc >= 0xf900 && cc < 0xfb00 )
16653                 ) {
16654                         return b;
16655                 }
16656                 return "&#"+cc+";" 
16657             });
16658             if(this.owner.fireEvent('beforesync', this, html) !== false){
16659                 this.el.dom.value = html;
16660                 this.owner.fireEvent('sync', this, html);
16661             }
16662         }
16663     },
16664
16665     /**
16666      * Protected method that will not generally be called directly. Pushes the value of the textarea
16667      * into the iframe editor.
16668      */
16669     pushValue : function(){
16670         if(this.initialized){
16671             var v = this.el.dom.value.trim();
16672             
16673 //            if(v.length < 1){
16674 //                v = '&#160;';
16675 //            }
16676             
16677             if(this.owner.fireEvent('beforepush', this, v) !== false){
16678                 var d = (this.doc.body || this.doc.documentElement);
16679                 d.innerHTML = v;
16680                 this.cleanUpPaste();
16681                 this.el.dom.value = d.innerHTML;
16682                 this.owner.fireEvent('push', this, v);
16683             }
16684         }
16685     },
16686
16687     // private
16688     deferFocus : function(){
16689         this.focus.defer(10, this);
16690     },
16691
16692     // doc'ed in Field
16693     focus : function(){
16694         if(this.win && !this.sourceEditMode){
16695             this.win.focus();
16696         }else{
16697             this.el.focus();
16698         }
16699     },
16700     
16701     assignDocWin: function()
16702     {
16703         var iframe = this.iframe;
16704         
16705          if(Roo.isIE){
16706             this.doc = iframe.contentWindow.document;
16707             this.win = iframe.contentWindow;
16708         } else {
16709 //            if (!Roo.get(this.frameId)) {
16710 //                return;
16711 //            }
16712 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16713 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16714             
16715             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16716                 return;
16717             }
16718             
16719             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16720             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16721         }
16722     },
16723     
16724     // private
16725     initEditor : function(){
16726         //console.log("INIT EDITOR");
16727         this.assignDocWin();
16728         
16729         
16730         
16731         this.doc.designMode="on";
16732         this.doc.open();
16733         this.doc.write(this.getDocMarkup());
16734         this.doc.close();
16735         
16736         var dbody = (this.doc.body || this.doc.documentElement);
16737         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16738         // this copies styles from the containing element into thsi one..
16739         // not sure why we need all of this..
16740         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16741         
16742         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16743         //ss['background-attachment'] = 'fixed'; // w3c
16744         dbody.bgProperties = 'fixed'; // ie
16745         //Roo.DomHelper.applyStyles(dbody, ss);
16746         Roo.EventManager.on(this.doc, {
16747             //'mousedown': this.onEditorEvent,
16748             'mouseup': this.onEditorEvent,
16749             'dblclick': this.onEditorEvent,
16750             'click': this.onEditorEvent,
16751             'keyup': this.onEditorEvent,
16752             buffer:100,
16753             scope: this
16754         });
16755         if(Roo.isGecko){
16756             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16757         }
16758         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16759             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16760         }
16761         this.initialized = true;
16762
16763         this.owner.fireEvent('initialize', this);
16764         this.pushValue();
16765     },
16766
16767     // private
16768     onDestroy : function(){
16769         
16770         
16771         
16772         if(this.rendered){
16773             
16774             //for (var i =0; i < this.toolbars.length;i++) {
16775             //    // fixme - ask toolbars for heights?
16776             //    this.toolbars[i].onDestroy();
16777            // }
16778             
16779             //this.wrap.dom.innerHTML = '';
16780             //this.wrap.remove();
16781         }
16782     },
16783
16784     // private
16785     onFirstFocus : function(){
16786         
16787         this.assignDocWin();
16788         
16789         
16790         this.activated = true;
16791          
16792     
16793         if(Roo.isGecko){ // prevent silly gecko errors
16794             this.win.focus();
16795             var s = this.win.getSelection();
16796             if(!s.focusNode || s.focusNode.nodeType != 3){
16797                 var r = s.getRangeAt(0);
16798                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16799                 r.collapse(true);
16800                 this.deferFocus();
16801             }
16802             try{
16803                 this.execCmd('useCSS', true);
16804                 this.execCmd('styleWithCSS', false);
16805             }catch(e){}
16806         }
16807         this.owner.fireEvent('activate', this);
16808     },
16809
16810     // private
16811     adjustFont: function(btn){
16812         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16813         //if(Roo.isSafari){ // safari
16814         //    adjust *= 2;
16815        // }
16816         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16817         if(Roo.isSafari){ // safari
16818             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16819             v =  (v < 10) ? 10 : v;
16820             v =  (v > 48) ? 48 : v;
16821             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16822             
16823         }
16824         
16825         
16826         v = Math.max(1, v+adjust);
16827         
16828         this.execCmd('FontSize', v  );
16829     },
16830
16831     onEditorEvent : function(e){
16832         this.owner.fireEvent('editorevent', this, e);
16833       //  this.updateToolbar();
16834         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16835     },
16836
16837     insertTag : function(tg)
16838     {
16839         // could be a bit smarter... -> wrap the current selected tRoo..
16840         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16841             
16842             range = this.createRange(this.getSelection());
16843             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16844             wrappingNode.appendChild(range.extractContents());
16845             range.insertNode(wrappingNode);
16846
16847             return;
16848             
16849             
16850             
16851         }
16852         this.execCmd("formatblock",   tg);
16853         
16854     },
16855     
16856     insertText : function(txt)
16857     {
16858         
16859         
16860         var range = this.createRange();
16861         range.deleteContents();
16862                //alert(Sender.getAttribute('label'));
16863                
16864         range.insertNode(this.doc.createTextNode(txt));
16865     } ,
16866     
16867      
16868
16869     /**
16870      * Executes a Midas editor command on the editor document and performs necessary focus and
16871      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16872      * @param {String} cmd The Midas command
16873      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16874      */
16875     relayCmd : function(cmd, value){
16876         this.win.focus();
16877         this.execCmd(cmd, value);
16878         this.owner.fireEvent('editorevent', this);
16879         //this.updateToolbar();
16880         this.owner.deferFocus();
16881     },
16882
16883     /**
16884      * Executes a Midas editor command directly on the editor document.
16885      * For visual commands, you should use {@link #relayCmd} instead.
16886      * <b>This should only be called after the editor is initialized.</b>
16887      * @param {String} cmd The Midas command
16888      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16889      */
16890     execCmd : function(cmd, value){
16891         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16892         this.syncValue();
16893     },
16894  
16895  
16896    
16897     /**
16898      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16899      * to insert tRoo.
16900      * @param {String} text | dom node.. 
16901      */
16902     insertAtCursor : function(text)
16903     {
16904         
16905         
16906         
16907         if(!this.activated){
16908             return;
16909         }
16910         /*
16911         if(Roo.isIE){
16912             this.win.focus();
16913             var r = this.doc.selection.createRange();
16914             if(r){
16915                 r.collapse(true);
16916                 r.pasteHTML(text);
16917                 this.syncValue();
16918                 this.deferFocus();
16919             
16920             }
16921             return;
16922         }
16923         */
16924         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16925             this.win.focus();
16926             
16927             
16928             // from jquery ui (MIT licenced)
16929             var range, node;
16930             var win = this.win;
16931             
16932             if (win.getSelection && win.getSelection().getRangeAt) {
16933                 range = win.getSelection().getRangeAt(0);
16934                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16935                 range.insertNode(node);
16936             } else if (win.document.selection && win.document.selection.createRange) {
16937                 // no firefox support
16938                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16939                 win.document.selection.createRange().pasteHTML(txt);
16940             } else {
16941                 // no firefox support
16942                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16943                 this.execCmd('InsertHTML', txt);
16944             } 
16945             
16946             this.syncValue();
16947             
16948             this.deferFocus();
16949         }
16950     },
16951  // private
16952     mozKeyPress : function(e){
16953         if(e.ctrlKey){
16954             var c = e.getCharCode(), cmd;
16955           
16956             if(c > 0){
16957                 c = String.fromCharCode(c).toLowerCase();
16958                 switch(c){
16959                     case 'b':
16960                         cmd = 'bold';
16961                         break;
16962                     case 'i':
16963                         cmd = 'italic';
16964                         break;
16965                     
16966                     case 'u':
16967                         cmd = 'underline';
16968                         break;
16969                     
16970                     case 'v':
16971                         this.cleanUpPaste.defer(100, this);
16972                         return;
16973                         
16974                 }
16975                 if(cmd){
16976                     this.win.focus();
16977                     this.execCmd(cmd);
16978                     this.deferFocus();
16979                     e.preventDefault();
16980                 }
16981                 
16982             }
16983         }
16984     },
16985
16986     // private
16987     fixKeys : function(){ // load time branching for fastest keydown performance
16988         if(Roo.isIE){
16989             return function(e){
16990                 var k = e.getKey(), r;
16991                 if(k == e.TAB){
16992                     e.stopEvent();
16993                     r = this.doc.selection.createRange();
16994                     if(r){
16995                         r.collapse(true);
16996                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16997                         this.deferFocus();
16998                     }
16999                     return;
17000                 }
17001                 
17002                 if(k == e.ENTER){
17003                     r = this.doc.selection.createRange();
17004                     if(r){
17005                         var target = r.parentElement();
17006                         if(!target || target.tagName.toLowerCase() != 'li'){
17007                             e.stopEvent();
17008                             r.pasteHTML('<br />');
17009                             r.collapse(false);
17010                             r.select();
17011                         }
17012                     }
17013                 }
17014                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17015                     this.cleanUpPaste.defer(100, this);
17016                     return;
17017                 }
17018                 
17019                 
17020             };
17021         }else if(Roo.isOpera){
17022             return function(e){
17023                 var k = e.getKey();
17024                 if(k == e.TAB){
17025                     e.stopEvent();
17026                     this.win.focus();
17027                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17028                     this.deferFocus();
17029                 }
17030                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17031                     this.cleanUpPaste.defer(100, this);
17032                     return;
17033                 }
17034                 
17035             };
17036         }else if(Roo.isSafari){
17037             return function(e){
17038                 var k = e.getKey();
17039                 
17040                 if(k == e.TAB){
17041                     e.stopEvent();
17042                     this.execCmd('InsertText','\t');
17043                     this.deferFocus();
17044                     return;
17045                 }
17046                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17047                     this.cleanUpPaste.defer(100, this);
17048                     return;
17049                 }
17050                 
17051              };
17052         }
17053     }(),
17054     
17055     getAllAncestors: function()
17056     {
17057         var p = this.getSelectedNode();
17058         var a = [];
17059         if (!p) {
17060             a.push(p); // push blank onto stack..
17061             p = this.getParentElement();
17062         }
17063         
17064         
17065         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17066             a.push(p);
17067             p = p.parentNode;
17068         }
17069         a.push(this.doc.body);
17070         return a;
17071     },
17072     lastSel : false,
17073     lastSelNode : false,
17074     
17075     
17076     getSelection : function() 
17077     {
17078         this.assignDocWin();
17079         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17080     },
17081     
17082     getSelectedNode: function() 
17083     {
17084         // this may only work on Gecko!!!
17085         
17086         // should we cache this!!!!
17087         
17088         
17089         
17090          
17091         var range = this.createRange(this.getSelection()).cloneRange();
17092         
17093         if (Roo.isIE) {
17094             var parent = range.parentElement();
17095             while (true) {
17096                 var testRange = range.duplicate();
17097                 testRange.moveToElementText(parent);
17098                 if (testRange.inRange(range)) {
17099                     break;
17100                 }
17101                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17102                     break;
17103                 }
17104                 parent = parent.parentElement;
17105             }
17106             return parent;
17107         }
17108         
17109         // is ancestor a text element.
17110         var ac =  range.commonAncestorContainer;
17111         if (ac.nodeType == 3) {
17112             ac = ac.parentNode;
17113         }
17114         
17115         var ar = ac.childNodes;
17116          
17117         var nodes = [];
17118         var other_nodes = [];
17119         var has_other_nodes = false;
17120         for (var i=0;i<ar.length;i++) {
17121             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17122                 continue;
17123             }
17124             // fullly contained node.
17125             
17126             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17127                 nodes.push(ar[i]);
17128                 continue;
17129             }
17130             
17131             // probably selected..
17132             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17133                 other_nodes.push(ar[i]);
17134                 continue;
17135             }
17136             // outer..
17137             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17138                 continue;
17139             }
17140             
17141             
17142             has_other_nodes = true;
17143         }
17144         if (!nodes.length && other_nodes.length) {
17145             nodes= other_nodes;
17146         }
17147         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17148             return false;
17149         }
17150         
17151         return nodes[0];
17152     },
17153     createRange: function(sel)
17154     {
17155         // this has strange effects when using with 
17156         // top toolbar - not sure if it's a great idea.
17157         //this.editor.contentWindow.focus();
17158         if (typeof sel != "undefined") {
17159             try {
17160                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17161             } catch(e) {
17162                 return this.doc.createRange();
17163             }
17164         } else {
17165             return this.doc.createRange();
17166         }
17167     },
17168     getParentElement: function()
17169     {
17170         
17171         this.assignDocWin();
17172         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17173         
17174         var range = this.createRange(sel);
17175          
17176         try {
17177             var p = range.commonAncestorContainer;
17178             while (p.nodeType == 3) { // text node
17179                 p = p.parentNode;
17180             }
17181             return p;
17182         } catch (e) {
17183             return null;
17184         }
17185     
17186     },
17187     /***
17188      *
17189      * Range intersection.. the hard stuff...
17190      *  '-1' = before
17191      *  '0' = hits..
17192      *  '1' = after.
17193      *         [ -- selected range --- ]
17194      *   [fail]                        [fail]
17195      *
17196      *    basically..
17197      *      if end is before start or  hits it. fail.
17198      *      if start is after end or hits it fail.
17199      *
17200      *   if either hits (but other is outside. - then it's not 
17201      *   
17202      *    
17203      **/
17204     
17205     
17206     // @see http://www.thismuchiknow.co.uk/?p=64.
17207     rangeIntersectsNode : function(range, node)
17208     {
17209         var nodeRange = node.ownerDocument.createRange();
17210         try {
17211             nodeRange.selectNode(node);
17212         } catch (e) {
17213             nodeRange.selectNodeContents(node);
17214         }
17215     
17216         var rangeStartRange = range.cloneRange();
17217         rangeStartRange.collapse(true);
17218     
17219         var rangeEndRange = range.cloneRange();
17220         rangeEndRange.collapse(false);
17221     
17222         var nodeStartRange = nodeRange.cloneRange();
17223         nodeStartRange.collapse(true);
17224     
17225         var nodeEndRange = nodeRange.cloneRange();
17226         nodeEndRange.collapse(false);
17227     
17228         return rangeStartRange.compareBoundaryPoints(
17229                  Range.START_TO_START, nodeEndRange) == -1 &&
17230                rangeEndRange.compareBoundaryPoints(
17231                  Range.START_TO_START, nodeStartRange) == 1;
17232         
17233          
17234     },
17235     rangeCompareNode : function(range, node)
17236     {
17237         var nodeRange = node.ownerDocument.createRange();
17238         try {
17239             nodeRange.selectNode(node);
17240         } catch (e) {
17241             nodeRange.selectNodeContents(node);
17242         }
17243         
17244         
17245         range.collapse(true);
17246     
17247         nodeRange.collapse(true);
17248      
17249         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17250         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17251          
17252         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17253         
17254         var nodeIsBefore   =  ss == 1;
17255         var nodeIsAfter    = ee == -1;
17256         
17257         if (nodeIsBefore && nodeIsAfter)
17258             return 0; // outer
17259         if (!nodeIsBefore && nodeIsAfter)
17260             return 1; //right trailed.
17261         
17262         if (nodeIsBefore && !nodeIsAfter)
17263             return 2;  // left trailed.
17264         // fully contined.
17265         return 3;
17266     },
17267
17268     // private? - in a new class?
17269     cleanUpPaste :  function()
17270     {
17271         // cleans up the whole document..
17272         Roo.log('cleanuppaste');
17273         
17274         this.cleanUpChildren(this.doc.body);
17275         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17276         if (clean != this.doc.body.innerHTML) {
17277             this.doc.body.innerHTML = clean;
17278         }
17279         
17280     },
17281     
17282     cleanWordChars : function(input) {// change the chars to hex code
17283         var he = Roo.HtmlEditorCore;
17284         
17285         var output = input;
17286         Roo.each(he.swapCodes, function(sw) { 
17287             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17288             
17289             output = output.replace(swapper, sw[1]);
17290         });
17291         
17292         return output;
17293     },
17294     
17295     
17296     cleanUpChildren : function (n)
17297     {
17298         if (!n.childNodes.length) {
17299             return;
17300         }
17301         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17302            this.cleanUpChild(n.childNodes[i]);
17303         }
17304     },
17305     
17306     
17307         
17308     
17309     cleanUpChild : function (node)
17310     {
17311         var ed = this;
17312         //console.log(node);
17313         if (node.nodeName == "#text") {
17314             // clean up silly Windows -- stuff?
17315             return; 
17316         }
17317         if (node.nodeName == "#comment") {
17318             node.parentNode.removeChild(node);
17319             // clean up silly Windows -- stuff?
17320             return; 
17321         }
17322         var lcname = node.tagName.toLowerCase();
17323         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17324         // whitelist of tags..
17325         
17326         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17327             // remove node.
17328             node.parentNode.removeChild(node);
17329             return;
17330             
17331         }
17332         
17333         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17334         
17335         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17336         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17337         
17338         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17339         //    remove_keep_children = true;
17340         //}
17341         
17342         if (remove_keep_children) {
17343             this.cleanUpChildren(node);
17344             // inserts everything just before this node...
17345             while (node.childNodes.length) {
17346                 var cn = node.childNodes[0];
17347                 node.removeChild(cn);
17348                 node.parentNode.insertBefore(cn, node);
17349             }
17350             node.parentNode.removeChild(node);
17351             return;
17352         }
17353         
17354         if (!node.attributes || !node.attributes.length) {
17355             this.cleanUpChildren(node);
17356             return;
17357         }
17358         
17359         function cleanAttr(n,v)
17360         {
17361             
17362             if (v.match(/^\./) || v.match(/^\//)) {
17363                 return;
17364             }
17365             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17366                 return;
17367             }
17368             if (v.match(/^#/)) {
17369                 return;
17370             }
17371 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17372             node.removeAttribute(n);
17373             
17374         }
17375         
17376         var cwhite = this.cwhite;
17377         var cblack = this.cblack;
17378             
17379         function cleanStyle(n,v)
17380         {
17381             if (v.match(/expression/)) { //XSS?? should we even bother..
17382                 node.removeAttribute(n);
17383                 return;
17384             }
17385             
17386             var parts = v.split(/;/);
17387             var clean = [];
17388             
17389             Roo.each(parts, function(p) {
17390                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17391                 if (!p.length) {
17392                     return true;
17393                 }
17394                 var l = p.split(':').shift().replace(/\s+/g,'');
17395                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17396                 
17397                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17398 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17399                     //node.removeAttribute(n);
17400                     return true;
17401                 }
17402                 //Roo.log()
17403                 // only allow 'c whitelisted system attributes'
17404                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17405 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17406                     //node.removeAttribute(n);
17407                     return true;
17408                 }
17409                 
17410                 
17411                  
17412                 
17413                 clean.push(p);
17414                 return true;
17415             });
17416             if (clean.length) { 
17417                 node.setAttribute(n, clean.join(';'));
17418             } else {
17419                 node.removeAttribute(n);
17420             }
17421             
17422         }
17423         
17424         
17425         for (var i = node.attributes.length-1; i > -1 ; i--) {
17426             var a = node.attributes[i];
17427             //console.log(a);
17428             
17429             if (a.name.toLowerCase().substr(0,2)=='on')  {
17430                 node.removeAttribute(a.name);
17431                 continue;
17432             }
17433             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17434                 node.removeAttribute(a.name);
17435                 continue;
17436             }
17437             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17438                 cleanAttr(a.name,a.value); // fixme..
17439                 continue;
17440             }
17441             if (a.name == 'style') {
17442                 cleanStyle(a.name,a.value);
17443                 continue;
17444             }
17445             /// clean up MS crap..
17446             // tecnically this should be a list of valid class'es..
17447             
17448             
17449             if (a.name == 'class') {
17450                 if (a.value.match(/^Mso/)) {
17451                     node.className = '';
17452                 }
17453                 
17454                 if (a.value.match(/body/)) {
17455                     node.className = '';
17456                 }
17457                 continue;
17458             }
17459             
17460             // style cleanup!?
17461             // class cleanup?
17462             
17463         }
17464         
17465         
17466         this.cleanUpChildren(node);
17467         
17468         
17469     },
17470     /**
17471      * Clean up MS wordisms...
17472      */
17473     cleanWord : function(node)
17474     {
17475         var _t = this;
17476         var cleanWordChildren = function()
17477         {
17478             if (!node.childNodes.length) {
17479                 return;
17480             }
17481             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17482                _t.cleanWord(node.childNodes[i]);
17483             }
17484         }
17485         
17486         
17487         if (!node) {
17488             this.cleanWord(this.doc.body);
17489             return;
17490         }
17491         if (node.nodeName == "#text") {
17492             // clean up silly Windows -- stuff?
17493             return; 
17494         }
17495         if (node.nodeName == "#comment") {
17496             node.parentNode.removeChild(node);
17497             // clean up silly Windows -- stuff?
17498             return; 
17499         }
17500         
17501         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17502             node.parentNode.removeChild(node);
17503             return;
17504         }
17505         
17506         // remove - but keep children..
17507         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17508             while (node.childNodes.length) {
17509                 var cn = node.childNodes[0];
17510                 node.removeChild(cn);
17511                 node.parentNode.insertBefore(cn, node);
17512             }
17513             node.parentNode.removeChild(node);
17514             cleanWordChildren();
17515             return;
17516         }
17517         // clean styles
17518         if (node.className.length) {
17519             
17520             var cn = node.className.split(/\W+/);
17521             var cna = [];
17522             Roo.each(cn, function(cls) {
17523                 if (cls.match(/Mso[a-zA-Z]+/)) {
17524                     return;
17525                 }
17526                 cna.push(cls);
17527             });
17528             node.className = cna.length ? cna.join(' ') : '';
17529             if (!cna.length) {
17530                 node.removeAttribute("class");
17531             }
17532         }
17533         
17534         if (node.hasAttribute("lang")) {
17535             node.removeAttribute("lang");
17536         }
17537         
17538         if (node.hasAttribute("style")) {
17539             
17540             var styles = node.getAttribute("style").split(";");
17541             var nstyle = [];
17542             Roo.each(styles, function(s) {
17543                 if (!s.match(/:/)) {
17544                     return;
17545                 }
17546                 var kv = s.split(":");
17547                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17548                     return;
17549                 }
17550                 // what ever is left... we allow.
17551                 nstyle.push(s);
17552             });
17553             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17554             if (!nstyle.length) {
17555                 node.removeAttribute('style');
17556             }
17557         }
17558         
17559         cleanWordChildren();
17560         
17561         
17562     },
17563     domToHTML : function(currentElement, depth, nopadtext) {
17564         
17565         depth = depth || 0;
17566         nopadtext = nopadtext || false;
17567     
17568         if (!currentElement) {
17569             return this.domToHTML(this.doc.body);
17570         }
17571         
17572         //Roo.log(currentElement);
17573         var j;
17574         var allText = false;
17575         var nodeName = currentElement.nodeName;
17576         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17577         
17578         if  (nodeName == '#text') {
17579             return currentElement.nodeValue;
17580         }
17581         
17582         
17583         var ret = '';
17584         if (nodeName != 'BODY') {
17585              
17586             var i = 0;
17587             // Prints the node tagName, such as <A>, <IMG>, etc
17588             if (tagName) {
17589                 var attr = [];
17590                 for(i = 0; i < currentElement.attributes.length;i++) {
17591                     // quoting?
17592                     var aname = currentElement.attributes.item(i).name;
17593                     if (!currentElement.attributes.item(i).value.length) {
17594                         continue;
17595                     }
17596                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17597                 }
17598                 
17599                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17600             } 
17601             else {
17602                 
17603                 // eack
17604             }
17605         } else {
17606             tagName = false;
17607         }
17608         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17609             return ret;
17610         }
17611         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17612             nopadtext = true;
17613         }
17614         
17615         
17616         // Traverse the tree
17617         i = 0;
17618         var currentElementChild = currentElement.childNodes.item(i);
17619         var allText = true;
17620         var innerHTML  = '';
17621         lastnode = '';
17622         while (currentElementChild) {
17623             // Formatting code (indent the tree so it looks nice on the screen)
17624             var nopad = nopadtext;
17625             if (lastnode == 'SPAN') {
17626                 nopad  = true;
17627             }
17628             // text
17629             if  (currentElementChild.nodeName == '#text') {
17630                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17631                 if (!nopad && toadd.length > 80) {
17632                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17633                 }
17634                 innerHTML  += toadd;
17635                 
17636                 i++;
17637                 currentElementChild = currentElement.childNodes.item(i);
17638                 lastNode = '';
17639                 continue;
17640             }
17641             allText = false;
17642             
17643             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17644                 
17645             // Recursively traverse the tree structure of the child node
17646             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17647             lastnode = currentElementChild.nodeName;
17648             i++;
17649             currentElementChild=currentElement.childNodes.item(i);
17650         }
17651         
17652         ret += innerHTML;
17653         
17654         if (!allText) {
17655                 // The remaining code is mostly for formatting the tree
17656             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17657         }
17658         
17659         
17660         if (tagName) {
17661             ret+= "</"+tagName+">";
17662         }
17663         return ret;
17664         
17665     },
17666         
17667     applyBlacklists : function()
17668     {
17669         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17670         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17671         
17672         this.white = [];
17673         this.black = [];
17674         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17675             if (b.indexOf(tag) > -1) {
17676                 return;
17677             }
17678             this.white.push(tag);
17679             
17680         }, this);
17681         
17682         Roo.each(w, function(tag) {
17683             if (b.indexOf(tag) > -1) {
17684                 return;
17685             }
17686             if (this.white.indexOf(tag) > -1) {
17687                 return;
17688             }
17689             this.white.push(tag);
17690             
17691         }, this);
17692         
17693         
17694         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17695             if (w.indexOf(tag) > -1) {
17696                 return;
17697             }
17698             this.black.push(tag);
17699             
17700         }, this);
17701         
17702         Roo.each(b, function(tag) {
17703             if (w.indexOf(tag) > -1) {
17704                 return;
17705             }
17706             if (this.black.indexOf(tag) > -1) {
17707                 return;
17708             }
17709             this.black.push(tag);
17710             
17711         }, this);
17712         
17713         
17714         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17715         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17716         
17717         this.cwhite = [];
17718         this.cblack = [];
17719         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17720             if (b.indexOf(tag) > -1) {
17721                 return;
17722             }
17723             this.cwhite.push(tag);
17724             
17725         }, this);
17726         
17727         Roo.each(w, function(tag) {
17728             if (b.indexOf(tag) > -1) {
17729                 return;
17730             }
17731             if (this.cwhite.indexOf(tag) > -1) {
17732                 return;
17733             }
17734             this.cwhite.push(tag);
17735             
17736         }, this);
17737         
17738         
17739         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17740             if (w.indexOf(tag) > -1) {
17741                 return;
17742             }
17743             this.cblack.push(tag);
17744             
17745         }, this);
17746         
17747         Roo.each(b, function(tag) {
17748             if (w.indexOf(tag) > -1) {
17749                 return;
17750             }
17751             if (this.cblack.indexOf(tag) > -1) {
17752                 return;
17753             }
17754             this.cblack.push(tag);
17755             
17756         }, this);
17757     }
17758     
17759     // hide stuff that is not compatible
17760     /**
17761      * @event blur
17762      * @hide
17763      */
17764     /**
17765      * @event change
17766      * @hide
17767      */
17768     /**
17769      * @event focus
17770      * @hide
17771      */
17772     /**
17773      * @event specialkey
17774      * @hide
17775      */
17776     /**
17777      * @cfg {String} fieldClass @hide
17778      */
17779     /**
17780      * @cfg {String} focusClass @hide
17781      */
17782     /**
17783      * @cfg {String} autoCreate @hide
17784      */
17785     /**
17786      * @cfg {String} inputType @hide
17787      */
17788     /**
17789      * @cfg {String} invalidClass @hide
17790      */
17791     /**
17792      * @cfg {String} invalidText @hide
17793      */
17794     /**
17795      * @cfg {String} msgFx @hide
17796      */
17797     /**
17798      * @cfg {String} validateOnBlur @hide
17799      */
17800 });
17801
17802 Roo.HtmlEditorCore.white = [
17803         'area', 'br', 'img', 'input', 'hr', 'wbr',
17804         
17805        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17806        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17807        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17808        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17809        'table',   'ul',         'xmp', 
17810        
17811        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17812       'thead',   'tr', 
17813      
17814       'dir', 'menu', 'ol', 'ul', 'dl',
17815        
17816       'embed',  'object'
17817 ];
17818
17819
17820 Roo.HtmlEditorCore.black = [
17821     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17822         'applet', // 
17823         'base',   'basefont', 'bgsound', 'blink',  'body', 
17824         'frame',  'frameset', 'head',    'html',   'ilayer', 
17825         'iframe', 'layer',  'link',     'meta',    'object',   
17826         'script', 'style' ,'title',  'xml' // clean later..
17827 ];
17828 Roo.HtmlEditorCore.clean = [
17829     'script', 'style', 'title', 'xml'
17830 ];
17831 Roo.HtmlEditorCore.remove = [
17832     'font'
17833 ];
17834 // attributes..
17835
17836 Roo.HtmlEditorCore.ablack = [
17837     'on'
17838 ];
17839     
17840 Roo.HtmlEditorCore.aclean = [ 
17841     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17842 ];
17843
17844 // protocols..
17845 Roo.HtmlEditorCore.pwhite= [
17846         'http',  'https',  'mailto'
17847 ];
17848
17849 // white listed style attributes.
17850 Roo.HtmlEditorCore.cwhite= [
17851       //  'text-align', /// default is to allow most things..
17852       
17853          
17854 //        'font-size'//??
17855 ];
17856
17857 // black listed style attributes.
17858 Roo.HtmlEditorCore.cblack= [
17859       //  'font-size' -- this can be set by the project 
17860 ];
17861
17862
17863 Roo.HtmlEditorCore.swapCodes   =[ 
17864     [    8211, "--" ], 
17865     [    8212, "--" ], 
17866     [    8216,  "'" ],  
17867     [    8217, "'" ],  
17868     [    8220, '"' ],  
17869     [    8221, '"' ],  
17870     [    8226, "*" ],  
17871     [    8230, "..." ]
17872 ]; 
17873
17874     /*
17875  * - LGPL
17876  *
17877  * HtmlEditor
17878  * 
17879  */
17880
17881 /**
17882  * @class Roo.bootstrap.HtmlEditor
17883  * @extends Roo.bootstrap.TextArea
17884  * Bootstrap HtmlEditor class
17885
17886  * @constructor
17887  * Create a new HtmlEditor
17888  * @param {Object} config The config object
17889  */
17890
17891 Roo.bootstrap.HtmlEditor = function(config){
17892     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17893     if (!this.toolbars) {
17894         this.toolbars = [];
17895     }
17896     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17897     this.addEvents({
17898             /**
17899              * @event initialize
17900              * Fires when the editor is fully initialized (including the iframe)
17901              * @param {HtmlEditor} this
17902              */
17903             initialize: true,
17904             /**
17905              * @event activate
17906              * Fires when the editor is first receives the focus. Any insertion must wait
17907              * until after this event.
17908              * @param {HtmlEditor} this
17909              */
17910             activate: true,
17911              /**
17912              * @event beforesync
17913              * Fires before the textarea is updated with content from the editor iframe. Return false
17914              * to cancel the sync.
17915              * @param {HtmlEditor} this
17916              * @param {String} html
17917              */
17918             beforesync: true,
17919              /**
17920              * @event beforepush
17921              * Fires before the iframe editor is updated with content from the textarea. Return false
17922              * to cancel the push.
17923              * @param {HtmlEditor} this
17924              * @param {String} html
17925              */
17926             beforepush: true,
17927              /**
17928              * @event sync
17929              * Fires when the textarea is updated with content from the editor iframe.
17930              * @param {HtmlEditor} this
17931              * @param {String} html
17932              */
17933             sync: true,
17934              /**
17935              * @event push
17936              * Fires when the iframe editor is updated with content from the textarea.
17937              * @param {HtmlEditor} this
17938              * @param {String} html
17939              */
17940             push: true,
17941              /**
17942              * @event editmodechange
17943              * Fires when the editor switches edit modes
17944              * @param {HtmlEditor} this
17945              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17946              */
17947             editmodechange: true,
17948             /**
17949              * @event editorevent
17950              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17951              * @param {HtmlEditor} this
17952              */
17953             editorevent: true,
17954             /**
17955              * @event firstfocus
17956              * Fires when on first focus - needed by toolbars..
17957              * @param {HtmlEditor} this
17958              */
17959             firstfocus: true,
17960             /**
17961              * @event autosave
17962              * Auto save the htmlEditor value as a file into Events
17963              * @param {HtmlEditor} this
17964              */
17965             autosave: true,
17966             /**
17967              * @event savedpreview
17968              * preview the saved version of htmlEditor
17969              * @param {HtmlEditor} this
17970              */
17971             savedpreview: true
17972         });
17973 };
17974
17975
17976 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17977     
17978     
17979       /**
17980      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17981      */
17982     toolbars : false,
17983    
17984      /**
17985      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17986      *                        Roo.resizable.
17987      */
17988     resizable : false,
17989      /**
17990      * @cfg {Number} height (in pixels)
17991      */   
17992     height: 300,
17993    /**
17994      * @cfg {Number} width (in pixels)
17995      */   
17996     width: false,
17997     
17998     /**
17999      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18000      * 
18001      */
18002     stylesheets: false,
18003     
18004     // id of frame..
18005     frameId: false,
18006     
18007     // private properties
18008     validationEvent : false,
18009     deferHeight: true,
18010     initialized : false,
18011     activated : false,
18012     
18013     onFocus : Roo.emptyFn,
18014     iframePad:3,
18015     hideMode:'offsets',
18016     
18017     
18018     tbContainer : false,
18019     
18020     toolbarContainer :function() {
18021         return this.wrap.select('.x-html-editor-tb',true).first();
18022     },
18023
18024     /**
18025      * Protected method that will not generally be called directly. It
18026      * is called when the editor creates its toolbar. Override this method if you need to
18027      * add custom toolbar buttons.
18028      * @param {HtmlEditor} editor
18029      */
18030     createToolbar : function(){
18031         
18032         Roo.log("create toolbars");
18033         
18034         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18035         this.toolbars[0].render(this.toolbarContainer());
18036         
18037         return;
18038         
18039 //        if (!editor.toolbars || !editor.toolbars.length) {
18040 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18041 //        }
18042 //        
18043 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18044 //            editor.toolbars[i] = Roo.factory(
18045 //                    typeof(editor.toolbars[i]) == 'string' ?
18046 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18047 //                Roo.bootstrap.HtmlEditor);
18048 //            editor.toolbars[i].init(editor);
18049 //        }
18050     },
18051
18052      
18053     // private
18054     onRender : function(ct, position)
18055     {
18056        // Roo.log("Call onRender: " + this.xtype);
18057         var _t = this;
18058         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18059       
18060         this.wrap = this.inputEl().wrap({
18061             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18062         });
18063         
18064         this.editorcore.onRender(ct, position);
18065          
18066         if (this.resizable) {
18067             this.resizeEl = new Roo.Resizable(this.wrap, {
18068                 pinned : true,
18069                 wrap: true,
18070                 dynamic : true,
18071                 minHeight : this.height,
18072                 height: this.height,
18073                 handles : this.resizable,
18074                 width: this.width,
18075                 listeners : {
18076                     resize : function(r, w, h) {
18077                         _t.onResize(w,h); // -something
18078                     }
18079                 }
18080             });
18081             
18082         }
18083         this.createToolbar(this);
18084        
18085         
18086         if(!this.width && this.resizable){
18087             this.setSize(this.wrap.getSize());
18088         }
18089         if (this.resizeEl) {
18090             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18091             // should trigger onReize..
18092         }
18093         
18094     },
18095
18096     // private
18097     onResize : function(w, h)
18098     {
18099         Roo.log('resize: ' +w + ',' + h );
18100         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18101         var ew = false;
18102         var eh = false;
18103         
18104         if(this.inputEl() ){
18105             if(typeof w == 'number'){
18106                 var aw = w - this.wrap.getFrameWidth('lr');
18107                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18108                 ew = aw;
18109             }
18110             if(typeof h == 'number'){
18111                  var tbh = -11;  // fixme it needs to tool bar size!
18112                 for (var i =0; i < this.toolbars.length;i++) {
18113                     // fixme - ask toolbars for heights?
18114                     tbh += this.toolbars[i].el.getHeight();
18115                     //if (this.toolbars[i].footer) {
18116                     //    tbh += this.toolbars[i].footer.el.getHeight();
18117                     //}
18118                 }
18119               
18120                 
18121                 
18122                 
18123                 
18124                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18125                 ah -= 5; // knock a few pixes off for look..
18126                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18127                 var eh = ah;
18128             }
18129         }
18130         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18131         this.editorcore.onResize(ew,eh);
18132         
18133     },
18134
18135     /**
18136      * Toggles the editor between standard and source edit mode.
18137      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18138      */
18139     toggleSourceEdit : function(sourceEditMode)
18140     {
18141         this.editorcore.toggleSourceEdit(sourceEditMode);
18142         
18143         if(this.editorcore.sourceEditMode){
18144             Roo.log('editor - showing textarea');
18145             
18146 //            Roo.log('in');
18147 //            Roo.log(this.syncValue());
18148             this.syncValue();
18149             this.inputEl().removeClass(['hide', 'x-hidden']);
18150             this.inputEl().dom.removeAttribute('tabIndex');
18151             this.inputEl().focus();
18152         }else{
18153             Roo.log('editor - hiding textarea');
18154 //            Roo.log('out')
18155 //            Roo.log(this.pushValue()); 
18156             this.pushValue();
18157             
18158             this.inputEl().addClass(['hide', 'x-hidden']);
18159             this.inputEl().dom.setAttribute('tabIndex', -1);
18160             //this.deferFocus();
18161         }
18162          
18163         if(this.resizable){
18164             this.setSize(this.wrap.getSize());
18165         }
18166         
18167         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18168     },
18169  
18170     // private (for BoxComponent)
18171     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18172
18173     // private (for BoxComponent)
18174     getResizeEl : function(){
18175         return this.wrap;
18176     },
18177
18178     // private (for BoxComponent)
18179     getPositionEl : function(){
18180         return this.wrap;
18181     },
18182
18183     // private
18184     initEvents : function(){
18185         this.originalValue = this.getValue();
18186     },
18187
18188 //    /**
18189 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18190 //     * @method
18191 //     */
18192 //    markInvalid : Roo.emptyFn,
18193 //    /**
18194 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18195 //     * @method
18196 //     */
18197 //    clearInvalid : Roo.emptyFn,
18198
18199     setValue : function(v){
18200         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18201         this.editorcore.pushValue();
18202     },
18203
18204      
18205     // private
18206     deferFocus : function(){
18207         this.focus.defer(10, this);
18208     },
18209
18210     // doc'ed in Field
18211     focus : function(){
18212         this.editorcore.focus();
18213         
18214     },
18215       
18216
18217     // private
18218     onDestroy : function(){
18219         
18220         
18221         
18222         if(this.rendered){
18223             
18224             for (var i =0; i < this.toolbars.length;i++) {
18225                 // fixme - ask toolbars for heights?
18226                 this.toolbars[i].onDestroy();
18227             }
18228             
18229             this.wrap.dom.innerHTML = '';
18230             this.wrap.remove();
18231         }
18232     },
18233
18234     // private
18235     onFirstFocus : function(){
18236         //Roo.log("onFirstFocus");
18237         this.editorcore.onFirstFocus();
18238          for (var i =0; i < this.toolbars.length;i++) {
18239             this.toolbars[i].onFirstFocus();
18240         }
18241         
18242     },
18243     
18244     // private
18245     syncValue : function()
18246     {   
18247         this.editorcore.syncValue();
18248     },
18249     
18250     pushValue : function()
18251     {   
18252         this.editorcore.pushValue();
18253     }
18254      
18255     
18256     // hide stuff that is not compatible
18257     /**
18258      * @event blur
18259      * @hide
18260      */
18261     /**
18262      * @event change
18263      * @hide
18264      */
18265     /**
18266      * @event focus
18267      * @hide
18268      */
18269     /**
18270      * @event specialkey
18271      * @hide
18272      */
18273     /**
18274      * @cfg {String} fieldClass @hide
18275      */
18276     /**
18277      * @cfg {String} focusClass @hide
18278      */
18279     /**
18280      * @cfg {String} autoCreate @hide
18281      */
18282     /**
18283      * @cfg {String} inputType @hide
18284      */
18285     /**
18286      * @cfg {String} invalidClass @hide
18287      */
18288     /**
18289      * @cfg {String} invalidText @hide
18290      */
18291     /**
18292      * @cfg {String} msgFx @hide
18293      */
18294     /**
18295      * @cfg {String} validateOnBlur @hide
18296      */
18297 });
18298  
18299     
18300    
18301    
18302    
18303       
18304 Roo.namespace('Roo.bootstrap.htmleditor');
18305 /**
18306  * @class Roo.bootstrap.HtmlEditorToolbar1
18307  * Basic Toolbar
18308  * 
18309  * Usage:
18310  *
18311  new Roo.bootstrap.HtmlEditor({
18312     ....
18313     toolbars : [
18314         new Roo.bootstrap.HtmlEditorToolbar1({
18315             disable : { fonts: 1 , format: 1, ..., ... , ...],
18316             btns : [ .... ]
18317         })
18318     }
18319      
18320  * 
18321  * @cfg {Object} disable List of elements to disable..
18322  * @cfg {Array} btns List of additional buttons.
18323  * 
18324  * 
18325  * NEEDS Extra CSS? 
18326  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18327  */
18328  
18329 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18330 {
18331     
18332     Roo.apply(this, config);
18333     
18334     // default disabled, based on 'good practice'..
18335     this.disable = this.disable || {};
18336     Roo.applyIf(this.disable, {
18337         fontSize : true,
18338         colors : true,
18339         specialElements : true
18340     });
18341     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18342     
18343     this.editor = config.editor;
18344     this.editorcore = config.editor.editorcore;
18345     
18346     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18347     
18348     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18349     // dont call parent... till later.
18350 }
18351 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18352      
18353     bar : true,
18354     
18355     editor : false,
18356     editorcore : false,
18357     
18358     
18359     formats : [
18360         "p" ,  
18361         "h1","h2","h3","h4","h5","h6", 
18362         "pre", "code", 
18363         "abbr", "acronym", "address", "cite", "samp", "var",
18364         'div','span'
18365     ],
18366     
18367     onRender : function(ct, position)
18368     {
18369        // Roo.log("Call onRender: " + this.xtype);
18370         
18371        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18372        Roo.log(this.el);
18373        this.el.dom.style.marginBottom = '0';
18374        var _this = this;
18375        var editorcore = this.editorcore;
18376        var editor= this.editor;
18377        
18378        var children = [];
18379        var btn = function(id,cmd , toggle, handler){
18380        
18381             var  event = toggle ? 'toggle' : 'click';
18382        
18383             var a = {
18384                 size : 'sm',
18385                 xtype: 'Button',
18386                 xns: Roo.bootstrap,
18387                 glyphicon : id,
18388                 cmd : id || cmd,
18389                 enableToggle:toggle !== false,
18390                 //html : 'submit'
18391                 pressed : toggle ? false : null,
18392                 listeners : {}
18393             }
18394             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18395                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18396             }
18397             children.push(a);
18398             return a;
18399        }
18400         
18401         var style = {
18402                 xtype: 'Button',
18403                 size : 'sm',
18404                 xns: Roo.bootstrap,
18405                 glyphicon : 'font',
18406                 //html : 'submit'
18407                 menu : {
18408                     xtype: 'Menu',
18409                     xns: Roo.bootstrap,
18410                     items:  []
18411                 }
18412         };
18413         Roo.each(this.formats, function(f) {
18414             style.menu.items.push({
18415                 xtype :'MenuItem',
18416                 xns: Roo.bootstrap,
18417                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18418                 tagname : f,
18419                 listeners : {
18420                     click : function()
18421                     {
18422                         editorcore.insertTag(this.tagname);
18423                         editor.focus();
18424                     }
18425                 }
18426                 
18427             });
18428         });
18429          children.push(style);   
18430             
18431             
18432         btn('bold',false,true);
18433         btn('italic',false,true);
18434         btn('align-left', 'justifyleft',true);
18435         btn('align-center', 'justifycenter',true);
18436         btn('align-right' , 'justifyright',true);
18437         btn('link', false, false, function(btn) {
18438             //Roo.log("create link?");
18439             var url = prompt(this.createLinkText, this.defaultLinkValue);
18440             if(url && url != 'http:/'+'/'){
18441                 this.editorcore.relayCmd('createlink', url);
18442             }
18443         }),
18444         btn('list','insertunorderedlist',true);
18445         btn('pencil', false,true, function(btn){
18446                 Roo.log(this);
18447                 
18448                 this.toggleSourceEdit(btn.pressed);
18449         });
18450         /*
18451         var cog = {
18452                 xtype: 'Button',
18453                 size : 'sm',
18454                 xns: Roo.bootstrap,
18455                 glyphicon : 'cog',
18456                 //html : 'submit'
18457                 menu : {
18458                     xtype: 'Menu',
18459                     xns: Roo.bootstrap,
18460                     items:  []
18461                 }
18462         };
18463         
18464         cog.menu.items.push({
18465             xtype :'MenuItem',
18466             xns: Roo.bootstrap,
18467             html : Clean styles,
18468             tagname : f,
18469             listeners : {
18470                 click : function()
18471                 {
18472                     editorcore.insertTag(this.tagname);
18473                     editor.focus();
18474                 }
18475             }
18476             
18477         });
18478        */
18479         
18480          
18481        this.xtype = 'NavSimplebar';
18482         
18483         for(var i=0;i< children.length;i++) {
18484             
18485             this.buttons.add(this.addxtypeChild(children[i]));
18486             
18487         }
18488         
18489         editor.on('editorevent', this.updateToolbar, this);
18490     },
18491     onBtnClick : function(id)
18492     {
18493        this.editorcore.relayCmd(id);
18494        this.editorcore.focus();
18495     },
18496     
18497     /**
18498      * Protected method that will not generally be called directly. It triggers
18499      * a toolbar update by reading the markup state of the current selection in the editor.
18500      */
18501     updateToolbar: function(){
18502
18503         if(!this.editorcore.activated){
18504             this.editor.onFirstFocus(); // is this neeed?
18505             return;
18506         }
18507
18508         var btns = this.buttons; 
18509         var doc = this.editorcore.doc;
18510         btns.get('bold').setActive(doc.queryCommandState('bold'));
18511         btns.get('italic').setActive(doc.queryCommandState('italic'));
18512         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18513         
18514         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18515         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18516         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18517         
18518         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18519         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18520          /*
18521         
18522         var ans = this.editorcore.getAllAncestors();
18523         if (this.formatCombo) {
18524             
18525             
18526             var store = this.formatCombo.store;
18527             this.formatCombo.setValue("");
18528             for (var i =0; i < ans.length;i++) {
18529                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18530                     // select it..
18531                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18532                     break;
18533                 }
18534             }
18535         }
18536         
18537         
18538         
18539         // hides menus... - so this cant be on a menu...
18540         Roo.bootstrap.MenuMgr.hideAll();
18541         */
18542         Roo.bootstrap.MenuMgr.hideAll();
18543         //this.editorsyncValue();
18544     },
18545     onFirstFocus: function() {
18546         this.buttons.each(function(item){
18547            item.enable();
18548         });
18549     },
18550     toggleSourceEdit : function(sourceEditMode){
18551         
18552           
18553         if(sourceEditMode){
18554             Roo.log("disabling buttons");
18555            this.buttons.each( function(item){
18556                 if(item.cmd != 'pencil'){
18557                     item.disable();
18558                 }
18559             });
18560           
18561         }else{
18562             Roo.log("enabling buttons");
18563             if(this.editorcore.initialized){
18564                 this.buttons.each( function(item){
18565                     item.enable();
18566                 });
18567             }
18568             
18569         }
18570         Roo.log("calling toggole on editor");
18571         // tell the editor that it's been pressed..
18572         this.editor.toggleSourceEdit(sourceEditMode);
18573        
18574     }
18575 });
18576
18577
18578
18579
18580
18581 /**
18582  * @class Roo.bootstrap.Table.AbstractSelectionModel
18583  * @extends Roo.util.Observable
18584  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18585  * implemented by descendant classes.  This class should not be directly instantiated.
18586  * @constructor
18587  */
18588 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18589     this.locked = false;
18590     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18591 };
18592
18593
18594 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18595     /** @ignore Called by the grid automatically. Do not call directly. */
18596     init : function(grid){
18597         this.grid = grid;
18598         this.initEvents();
18599     },
18600
18601     /**
18602      * Locks the selections.
18603      */
18604     lock : function(){
18605         this.locked = true;
18606     },
18607
18608     /**
18609      * Unlocks the selections.
18610      */
18611     unlock : function(){
18612         this.locked = false;
18613     },
18614
18615     /**
18616      * Returns true if the selections are locked.
18617      * @return {Boolean}
18618      */
18619     isLocked : function(){
18620         return this.locked;
18621     }
18622 });
18623 /**
18624  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18625  * @class Roo.bootstrap.Table.RowSelectionModel
18626  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18627  * It supports multiple selections and keyboard selection/navigation. 
18628  * @constructor
18629  * @param {Object} config
18630  */
18631
18632 Roo.bootstrap.Table.RowSelectionModel = function(config){
18633     Roo.apply(this, config);
18634     this.selections = new Roo.util.MixedCollection(false, function(o){
18635         return o.id;
18636     });
18637
18638     this.last = false;
18639     this.lastActive = false;
18640
18641     this.addEvents({
18642         /**
18643              * @event selectionchange
18644              * Fires when the selection changes
18645              * @param {SelectionModel} this
18646              */
18647             "selectionchange" : true,
18648         /**
18649              * @event afterselectionchange
18650              * Fires after the selection changes (eg. by key press or clicking)
18651              * @param {SelectionModel} this
18652              */
18653             "afterselectionchange" : true,
18654         /**
18655              * @event beforerowselect
18656              * Fires when a row is selected being selected, return false to cancel.
18657              * @param {SelectionModel} this
18658              * @param {Number} rowIndex The selected index
18659              * @param {Boolean} keepExisting False if other selections will be cleared
18660              */
18661             "beforerowselect" : true,
18662         /**
18663              * @event rowselect
18664              * Fires when a row is selected.
18665              * @param {SelectionModel} this
18666              * @param {Number} rowIndex The selected index
18667              * @param {Roo.data.Record} r The record
18668              */
18669             "rowselect" : true,
18670         /**
18671              * @event rowdeselect
18672              * Fires when a row is deselected.
18673              * @param {SelectionModel} this
18674              * @param {Number} rowIndex The selected index
18675              */
18676         "rowdeselect" : true
18677     });
18678     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18679     this.locked = false;
18680 };
18681
18682 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18683     /**
18684      * @cfg {Boolean} singleSelect
18685      * True to allow selection of only one row at a time (defaults to false)
18686      */
18687     singleSelect : false,
18688
18689     // private
18690     initEvents : function(){
18691
18692         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18693             this.grid.on("mousedown", this.handleMouseDown, this);
18694         }else{ // allow click to work like normal
18695             this.grid.on("rowclick", this.handleDragableRowClick, this);
18696         }
18697
18698         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18699             "up" : function(e){
18700                 if(!e.shiftKey){
18701                     this.selectPrevious(e.shiftKey);
18702                 }else if(this.last !== false && this.lastActive !== false){
18703                     var last = this.last;
18704                     this.selectRange(this.last,  this.lastActive-1);
18705                     this.grid.getView().focusRow(this.lastActive);
18706                     if(last !== false){
18707                         this.last = last;
18708                     }
18709                 }else{
18710                     this.selectFirstRow();
18711                 }
18712                 this.fireEvent("afterselectionchange", this);
18713             },
18714             "down" : function(e){
18715                 if(!e.shiftKey){
18716                     this.selectNext(e.shiftKey);
18717                 }else if(this.last !== false && this.lastActive !== false){
18718                     var last = this.last;
18719                     this.selectRange(this.last,  this.lastActive+1);
18720                     this.grid.getView().focusRow(this.lastActive);
18721                     if(last !== false){
18722                         this.last = last;
18723                     }
18724                 }else{
18725                     this.selectFirstRow();
18726                 }
18727                 this.fireEvent("afterselectionchange", this);
18728             },
18729             scope: this
18730         });
18731
18732         var view = this.grid.view;
18733         view.on("refresh", this.onRefresh, this);
18734         view.on("rowupdated", this.onRowUpdated, this);
18735         view.on("rowremoved", this.onRemove, this);
18736     },
18737
18738     // private
18739     onRefresh : function(){
18740         var ds = this.grid.dataSource, i, v = this.grid.view;
18741         var s = this.selections;
18742         s.each(function(r){
18743             if((i = ds.indexOfId(r.id)) != -1){
18744                 v.onRowSelect(i);
18745             }else{
18746                 s.remove(r);
18747             }
18748         });
18749     },
18750
18751     // private
18752     onRemove : function(v, index, r){
18753         this.selections.remove(r);
18754     },
18755
18756     // private
18757     onRowUpdated : function(v, index, r){
18758         if(this.isSelected(r)){
18759             v.onRowSelect(index);
18760         }
18761     },
18762
18763     /**
18764      * Select records.
18765      * @param {Array} records The records to select
18766      * @param {Boolean} keepExisting (optional) True to keep existing selections
18767      */
18768     selectRecords : function(records, keepExisting){
18769         if(!keepExisting){
18770             this.clearSelections();
18771         }
18772         var ds = this.grid.dataSource;
18773         for(var i = 0, len = records.length; i < len; i++){
18774             this.selectRow(ds.indexOf(records[i]), true);
18775         }
18776     },
18777
18778     /**
18779      * Gets the number of selected rows.
18780      * @return {Number}
18781      */
18782     getCount : function(){
18783         return this.selections.length;
18784     },
18785
18786     /**
18787      * Selects the first row in the grid.
18788      */
18789     selectFirstRow : function(){
18790         this.selectRow(0);
18791     },
18792
18793     /**
18794      * Select the last row.
18795      * @param {Boolean} keepExisting (optional) True to keep existing selections
18796      */
18797     selectLastRow : function(keepExisting){
18798         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18799     },
18800
18801     /**
18802      * Selects the row immediately following the last selected row.
18803      * @param {Boolean} keepExisting (optional) True to keep existing selections
18804      */
18805     selectNext : function(keepExisting){
18806         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18807             this.selectRow(this.last+1, keepExisting);
18808             this.grid.getView().focusRow(this.last);
18809         }
18810     },
18811
18812     /**
18813      * Selects the row that precedes the last selected row.
18814      * @param {Boolean} keepExisting (optional) True to keep existing selections
18815      */
18816     selectPrevious : function(keepExisting){
18817         if(this.last){
18818             this.selectRow(this.last-1, keepExisting);
18819             this.grid.getView().focusRow(this.last);
18820         }
18821     },
18822
18823     /**
18824      * Returns the selected records
18825      * @return {Array} Array of selected records
18826      */
18827     getSelections : function(){
18828         return [].concat(this.selections.items);
18829     },
18830
18831     /**
18832      * Returns the first selected record.
18833      * @return {Record}
18834      */
18835     getSelected : function(){
18836         return this.selections.itemAt(0);
18837     },
18838
18839
18840     /**
18841      * Clears all selections.
18842      */
18843     clearSelections : function(fast){
18844         if(this.locked) return;
18845         if(fast !== true){
18846             var ds = this.grid.dataSource;
18847             var s = this.selections;
18848             s.each(function(r){
18849                 this.deselectRow(ds.indexOfId(r.id));
18850             }, this);
18851             s.clear();
18852         }else{
18853             this.selections.clear();
18854         }
18855         this.last = false;
18856     },
18857
18858
18859     /**
18860      * Selects all rows.
18861      */
18862     selectAll : function(){
18863         if(this.locked) return;
18864         this.selections.clear();
18865         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18866             this.selectRow(i, true);
18867         }
18868     },
18869
18870     /**
18871      * Returns True if there is a selection.
18872      * @return {Boolean}
18873      */
18874     hasSelection : function(){
18875         return this.selections.length > 0;
18876     },
18877
18878     /**
18879      * Returns True if the specified row is selected.
18880      * @param {Number/Record} record The record or index of the record to check
18881      * @return {Boolean}
18882      */
18883     isSelected : function(index){
18884         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18885         return (r && this.selections.key(r.id) ? true : false);
18886     },
18887
18888     /**
18889      * Returns True if the specified record id is selected.
18890      * @param {String} id The id of record to check
18891      * @return {Boolean}
18892      */
18893     isIdSelected : function(id){
18894         return (this.selections.key(id) ? true : false);
18895     },
18896
18897     // private
18898     handleMouseDown : function(e, t){
18899         var view = this.grid.getView(), rowIndex;
18900         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18901             return;
18902         };
18903         if(e.shiftKey && this.last !== false){
18904             var last = this.last;
18905             this.selectRange(last, rowIndex, e.ctrlKey);
18906             this.last = last; // reset the last
18907             view.focusRow(rowIndex);
18908         }else{
18909             var isSelected = this.isSelected(rowIndex);
18910             if(e.button !== 0 && isSelected){
18911                 view.focusRow(rowIndex);
18912             }else if(e.ctrlKey && isSelected){
18913                 this.deselectRow(rowIndex);
18914             }else if(!isSelected){
18915                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18916                 view.focusRow(rowIndex);
18917             }
18918         }
18919         this.fireEvent("afterselectionchange", this);
18920     },
18921     // private
18922     handleDragableRowClick :  function(grid, rowIndex, e) 
18923     {
18924         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18925             this.selectRow(rowIndex, false);
18926             grid.view.focusRow(rowIndex);
18927              this.fireEvent("afterselectionchange", this);
18928         }
18929     },
18930     
18931     /**
18932      * Selects multiple rows.
18933      * @param {Array} rows Array of the indexes of the row to select
18934      * @param {Boolean} keepExisting (optional) True to keep existing selections
18935      */
18936     selectRows : function(rows, keepExisting){
18937         if(!keepExisting){
18938             this.clearSelections();
18939         }
18940         for(var i = 0, len = rows.length; i < len; i++){
18941             this.selectRow(rows[i], true);
18942         }
18943     },
18944
18945     /**
18946      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18947      * @param {Number} startRow The index of the first row in the range
18948      * @param {Number} endRow The index of the last row in the range
18949      * @param {Boolean} keepExisting (optional) True to retain existing selections
18950      */
18951     selectRange : function(startRow, endRow, keepExisting){
18952         if(this.locked) return;
18953         if(!keepExisting){
18954             this.clearSelections();
18955         }
18956         if(startRow <= endRow){
18957             for(var i = startRow; i <= endRow; i++){
18958                 this.selectRow(i, true);
18959             }
18960         }else{
18961             for(var i = startRow; i >= endRow; i--){
18962                 this.selectRow(i, true);
18963             }
18964         }
18965     },
18966
18967     /**
18968      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18969      * @param {Number} startRow The index of the first row in the range
18970      * @param {Number} endRow The index of the last row in the range
18971      */
18972     deselectRange : function(startRow, endRow, preventViewNotify){
18973         if(this.locked) return;
18974         for(var i = startRow; i <= endRow; i++){
18975             this.deselectRow(i, preventViewNotify);
18976         }
18977     },
18978
18979     /**
18980      * Selects a row.
18981      * @param {Number} row The index of the row to select
18982      * @param {Boolean} keepExisting (optional) True to keep existing selections
18983      */
18984     selectRow : function(index, keepExisting, preventViewNotify){
18985         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18986         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18987             if(!keepExisting || this.singleSelect){
18988                 this.clearSelections();
18989             }
18990             var r = this.grid.dataSource.getAt(index);
18991             this.selections.add(r);
18992             this.last = this.lastActive = index;
18993             if(!preventViewNotify){
18994                 this.grid.getView().onRowSelect(index);
18995             }
18996             this.fireEvent("rowselect", this, index, r);
18997             this.fireEvent("selectionchange", this);
18998         }
18999     },
19000
19001     /**
19002      * Deselects a row.
19003      * @param {Number} row The index of the row to deselect
19004      */
19005     deselectRow : function(index, preventViewNotify){
19006         if(this.locked) return;
19007         if(this.last == index){
19008             this.last = false;
19009         }
19010         if(this.lastActive == index){
19011             this.lastActive = false;
19012         }
19013         var r = this.grid.dataSource.getAt(index);
19014         this.selections.remove(r);
19015         if(!preventViewNotify){
19016             this.grid.getView().onRowDeselect(index);
19017         }
19018         this.fireEvent("rowdeselect", this, index);
19019         this.fireEvent("selectionchange", this);
19020     },
19021
19022     // private
19023     restoreLast : function(){
19024         if(this._last){
19025             this.last = this._last;
19026         }
19027     },
19028
19029     // private
19030     acceptsNav : function(row, col, cm){
19031         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19032     },
19033
19034     // private
19035     onEditorKey : function(field, e){
19036         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19037         if(k == e.TAB){
19038             e.stopEvent();
19039             ed.completeEdit();
19040             if(e.shiftKey){
19041                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19042             }else{
19043                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19044             }
19045         }else if(k == e.ENTER && !e.ctrlKey){
19046             e.stopEvent();
19047             ed.completeEdit();
19048             if(e.shiftKey){
19049                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19050             }else{
19051                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19052             }
19053         }else if(k == e.ESC){
19054             ed.cancelEdit();
19055         }
19056         if(newCell){
19057             g.startEditing(newCell[0], newCell[1]);
19058         }
19059     }
19060 });/*
19061  * Based on:
19062  * Ext JS Library 1.1.1
19063  * Copyright(c) 2006-2007, Ext JS, LLC.
19064  *
19065  * Originally Released Under LGPL - original licence link has changed is not relivant.
19066  *
19067  * Fork - LGPL
19068  * <script type="text/javascript">
19069  */
19070  
19071 /**
19072  * @class Roo.bootstrap.PagingToolbar
19073  * @extends Roo.Row
19074  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19075  * @constructor
19076  * Create a new PagingToolbar
19077  * @param {Object} config The config object
19078  */
19079 Roo.bootstrap.PagingToolbar = function(config)
19080 {
19081     // old args format still supported... - xtype is prefered..
19082         // created from xtype...
19083     var ds = config.dataSource;
19084     this.toolbarItems = [];
19085     if (config.items) {
19086         this.toolbarItems = config.items;
19087 //        config.items = [];
19088     }
19089     
19090     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19091     this.ds = ds;
19092     this.cursor = 0;
19093     if (ds) { 
19094         this.bind(ds);
19095     }
19096     
19097     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19098     
19099 };
19100
19101 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19102     /**
19103      * @cfg {Roo.data.Store} dataSource
19104      * The underlying data store providing the paged data
19105      */
19106     /**
19107      * @cfg {String/HTMLElement/Element} container
19108      * container The id or element that will contain the toolbar
19109      */
19110     /**
19111      * @cfg {Boolean} displayInfo
19112      * True to display the displayMsg (defaults to false)
19113      */
19114     /**
19115      * @cfg {Number} pageSize
19116      * The number of records to display per page (defaults to 20)
19117      */
19118     pageSize: 20,
19119     /**
19120      * @cfg {String} displayMsg
19121      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19122      */
19123     displayMsg : 'Displaying {0} - {1} of {2}',
19124     /**
19125      * @cfg {String} emptyMsg
19126      * The message to display when no records are found (defaults to "No data to display")
19127      */
19128     emptyMsg : 'No data to display',
19129     /**
19130      * Customizable piece of the default paging text (defaults to "Page")
19131      * @type String
19132      */
19133     beforePageText : "Page",
19134     /**
19135      * Customizable piece of the default paging text (defaults to "of %0")
19136      * @type String
19137      */
19138     afterPageText : "of {0}",
19139     /**
19140      * Customizable piece of the default paging text (defaults to "First Page")
19141      * @type String
19142      */
19143     firstText : "First Page",
19144     /**
19145      * Customizable piece of the default paging text (defaults to "Previous Page")
19146      * @type String
19147      */
19148     prevText : "Previous Page",
19149     /**
19150      * Customizable piece of the default paging text (defaults to "Next Page")
19151      * @type String
19152      */
19153     nextText : "Next Page",
19154     /**
19155      * Customizable piece of the default paging text (defaults to "Last Page")
19156      * @type String
19157      */
19158     lastText : "Last Page",
19159     /**
19160      * Customizable piece of the default paging text (defaults to "Refresh")
19161      * @type String
19162      */
19163     refreshText : "Refresh",
19164
19165     buttons : false,
19166     // private
19167     onRender : function(ct, position) 
19168     {
19169         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19170         this.navgroup.parentId = this.id;
19171         this.navgroup.onRender(this.el, null);
19172         // add the buttons to the navgroup
19173         
19174         if(this.displayInfo){
19175             Roo.log(this.el.select('ul.navbar-nav',true).first());
19176             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19177             this.displayEl = this.el.select('.x-paging-info', true).first();
19178 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19179 //            this.displayEl = navel.el.select('span',true).first();
19180         }
19181         
19182         var _this = this;
19183         
19184         if(this.buttons){
19185             Roo.each(_this.buttons, function(e){
19186                Roo.factory(e).onRender(_this.el, null);
19187             });
19188         }
19189             
19190         Roo.each(_this.toolbarItems, function(e) {
19191             _this.navgroup.addItem(e);
19192         });
19193         
19194         this.first = this.navgroup.addItem({
19195             tooltip: this.firstText,
19196             cls: "prev",
19197             icon : 'fa fa-backward',
19198             disabled: true,
19199             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19200         });
19201         
19202         this.prev =  this.navgroup.addItem({
19203             tooltip: this.prevText,
19204             cls: "prev",
19205             icon : 'fa fa-step-backward',
19206             disabled: true,
19207             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19208         });
19209     //this.addSeparator();
19210         
19211         
19212         var field = this.navgroup.addItem( {
19213             tagtype : 'span',
19214             cls : 'x-paging-position',
19215             
19216             html : this.beforePageText  +
19217                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19218                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19219          } ); //?? escaped?
19220         
19221         this.field = field.el.select('input', true).first();
19222         this.field.on("keydown", this.onPagingKeydown, this);
19223         this.field.on("focus", function(){this.dom.select();});
19224     
19225     
19226         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19227         //this.field.setHeight(18);
19228         //this.addSeparator();
19229         this.next = this.navgroup.addItem({
19230             tooltip: this.nextText,
19231             cls: "next",
19232             html : ' <i class="fa fa-step-forward">',
19233             disabled: true,
19234             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19235         });
19236         this.last = this.navgroup.addItem({
19237             tooltip: this.lastText,
19238             icon : 'fa fa-forward',
19239             cls: "next",
19240             disabled: true,
19241             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19242         });
19243     //this.addSeparator();
19244         this.loading = this.navgroup.addItem({
19245             tooltip: this.refreshText,
19246             icon: 'fa fa-refresh',
19247             
19248             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19249         });
19250
19251     },
19252
19253     // private
19254     updateInfo : function(){
19255         if(this.displayEl){
19256             var count = this.ds.getCount();
19257             var msg = count == 0 ?
19258                 this.emptyMsg :
19259                 String.format(
19260                     this.displayMsg,
19261                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19262                 );
19263             this.displayEl.update(msg);
19264         }
19265     },
19266
19267     // private
19268     onLoad : function(ds, r, o){
19269        this.cursor = o.params ? o.params.start : 0;
19270        var d = this.getPageData(),
19271             ap = d.activePage,
19272             ps = d.pages;
19273         
19274        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19275        this.field.dom.value = ap;
19276        this.first.setDisabled(ap == 1);
19277        this.prev.setDisabled(ap == 1);
19278        this.next.setDisabled(ap == ps);
19279        this.last.setDisabled(ap == ps);
19280        this.loading.enable();
19281        this.updateInfo();
19282     },
19283
19284     // private
19285     getPageData : function(){
19286         var total = this.ds.getTotalCount();
19287         return {
19288             total : total,
19289             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19290             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19291         };
19292     },
19293
19294     // private
19295     onLoadError : function(){
19296         this.loading.enable();
19297     },
19298
19299     // private
19300     onPagingKeydown : function(e){
19301         var k = e.getKey();
19302         var d = this.getPageData();
19303         if(k == e.RETURN){
19304             var v = this.field.dom.value, pageNum;
19305             if(!v || isNaN(pageNum = parseInt(v, 10))){
19306                 this.field.dom.value = d.activePage;
19307                 return;
19308             }
19309             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19310             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19311             e.stopEvent();
19312         }
19313         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
19314         {
19315           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19316           this.field.dom.value = pageNum;
19317           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19318           e.stopEvent();
19319         }
19320         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19321         {
19322           var v = this.field.dom.value, pageNum; 
19323           var increment = (e.shiftKey) ? 10 : 1;
19324           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19325             increment *= -1;
19326           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19327             this.field.dom.value = d.activePage;
19328             return;
19329           }
19330           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19331           {
19332             this.field.dom.value = parseInt(v, 10) + increment;
19333             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19334             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19335           }
19336           e.stopEvent();
19337         }
19338     },
19339
19340     // private
19341     beforeLoad : function(){
19342         if(this.loading){
19343             this.loading.disable();
19344         }
19345     },
19346
19347     // private
19348     onClick : function(which){
19349         var ds = this.ds;
19350         if (!ds) {
19351             return;
19352         }
19353         switch(which){
19354             case "first":
19355                 ds.load({params:{start: 0, limit: this.pageSize}});
19356             break;
19357             case "prev":
19358                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19359             break;
19360             case "next":
19361                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19362             break;
19363             case "last":
19364                 var total = ds.getTotalCount();
19365                 var extra = total % this.pageSize;
19366                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19367                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19368             break;
19369             case "refresh":
19370                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19371             break;
19372         }
19373     },
19374
19375     /**
19376      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19377      * @param {Roo.data.Store} store The data store to unbind
19378      */
19379     unbind : function(ds){
19380         ds.un("beforeload", this.beforeLoad, this);
19381         ds.un("load", this.onLoad, this);
19382         ds.un("loadexception", this.onLoadError, this);
19383         ds.un("remove", this.updateInfo, this);
19384         ds.un("add", this.updateInfo, this);
19385         this.ds = undefined;
19386     },
19387
19388     /**
19389      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19390      * @param {Roo.data.Store} store The data store to bind
19391      */
19392     bind : function(ds){
19393         ds.on("beforeload", this.beforeLoad, this);
19394         ds.on("load", this.onLoad, this);
19395         ds.on("loadexception", this.onLoadError, this);
19396         ds.on("remove", this.updateInfo, this);
19397         ds.on("add", this.updateInfo, this);
19398         this.ds = ds;
19399     }
19400 });/*
19401  * - LGPL
19402  *
19403  * element
19404  * 
19405  */
19406
19407 /**
19408  * @class Roo.bootstrap.MessageBar
19409  * @extends Roo.bootstrap.Component
19410  * Bootstrap MessageBar class
19411  * @cfg {String} html contents of the MessageBar
19412  * @cfg {String} weight (info | success | warning | danger) default info
19413  * @cfg {String} beforeClass insert the bar before the given class
19414  * @cfg {Boolean} closable (true | false) default false
19415  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19416  * 
19417  * @constructor
19418  * Create a new Element
19419  * @param {Object} config The config object
19420  */
19421
19422 Roo.bootstrap.MessageBar = function(config){
19423     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19424 };
19425
19426 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19427     
19428     html: '',
19429     weight: 'info',
19430     closable: false,
19431     fixed: false,
19432     beforeClass: 'bootstrap-sticky-wrap',
19433     
19434     getAutoCreate : function(){
19435         
19436         var cfg = {
19437             tag: 'div',
19438             cls: 'alert alert-dismissable alert-' + this.weight,
19439             cn: [
19440                 {
19441                     tag: 'span',
19442                     cls: 'message',
19443                     html: this.html || ''
19444                 }
19445             ]
19446         }
19447         
19448         if(this.fixed){
19449             cfg.cls += ' alert-messages-fixed';
19450         }
19451         
19452         if(this.closable){
19453             cfg.cn.push({
19454                 tag: 'button',
19455                 cls: 'close',
19456                 html: 'x'
19457             });
19458         }
19459         
19460         return cfg;
19461     },
19462     
19463     onRender : function(ct, position)
19464     {
19465         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19466         
19467         if(!this.el){
19468             var cfg = Roo.apply({},  this.getAutoCreate());
19469             cfg.id = Roo.id();
19470             
19471             if (this.cls) {
19472                 cfg.cls += ' ' + this.cls;
19473             }
19474             if (this.style) {
19475                 cfg.style = this.style;
19476             }
19477             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19478             
19479             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19480         }
19481         
19482         this.el.select('>button.close').on('click', this.hide, this);
19483         
19484     },
19485     
19486     show : function()
19487     {
19488         if (!this.rendered) {
19489             this.render();
19490         }
19491         
19492         this.el.show();
19493         
19494         this.fireEvent('show', this);
19495         
19496     },
19497     
19498     hide : function()
19499     {
19500         if (!this.rendered) {
19501             this.render();
19502         }
19503         
19504         this.el.hide();
19505         
19506         this.fireEvent('hide', this);
19507     },
19508     
19509     update : function()
19510     {
19511 //        var e = this.el.dom.firstChild;
19512 //        
19513 //        if(this.closable){
19514 //            e = e.nextSibling;
19515 //        }
19516 //        
19517 //        e.data = this.html || '';
19518
19519         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19520     }
19521    
19522 });
19523
19524  
19525
19526      /*
19527  * - LGPL
19528  *
19529  * Graph
19530  * 
19531  */
19532
19533
19534 /**
19535  * @class Roo.bootstrap.Graph
19536  * @extends Roo.bootstrap.Component
19537  * Bootstrap Graph class
19538 > Prameters
19539  -sm {number} sm 4
19540  -md {number} md 5
19541  @cfg {String} graphtype  bar | vbar | pie
19542  @cfg {number} g_x coodinator | centre x (pie)
19543  @cfg {number} g_y coodinator | centre y (pie)
19544  @cfg {number} g_r radius (pie)
19545  @cfg {number} g_height height of the chart (respected by all elements in the set)
19546  @cfg {number} g_width width of the chart (respected by all elements in the set)
19547  @cfg {Object} title The title of the chart
19548     
19549  -{Array}  values
19550  -opts (object) options for the chart 
19551      o {
19552      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19553      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19554      o vgutter (number)
19555      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
19556      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19557      o to
19558      o stretch (boolean)
19559      o }
19560  -opts (object) options for the pie
19561      o{
19562      o cut
19563      o startAngle (number)
19564      o endAngle (number)
19565      } 
19566  *
19567  * @constructor
19568  * Create a new Input
19569  * @param {Object} config The config object
19570  */
19571
19572 Roo.bootstrap.Graph = function(config){
19573     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19574     
19575     this.addEvents({
19576         // img events
19577         /**
19578          * @event click
19579          * The img click event for the img.
19580          * @param {Roo.EventObject} e
19581          */
19582         "click" : true
19583     });
19584 };
19585
19586 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19587     
19588     sm: 4,
19589     md: 5,
19590     graphtype: 'bar',
19591     g_height: 250,
19592     g_width: 400,
19593     g_x: 50,
19594     g_y: 50,
19595     g_r: 30,
19596     opts:{
19597         //g_colors: this.colors,
19598         g_type: 'soft',
19599         g_gutter: '20%'
19600
19601     },
19602     title : false,
19603
19604     getAutoCreate : function(){
19605         
19606         var cfg = {
19607             tag: 'div',
19608             html : null
19609         }
19610         
19611         
19612         return  cfg;
19613     },
19614
19615     onRender : function(ct,position){
19616         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19617         this.raphael = Raphael(this.el.dom);
19618         
19619                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19620                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19621                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19622                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19623                 /*
19624                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19625                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19626                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19627                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19628                 
19629                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19630                 r.barchart(330, 10, 300, 220, data1);
19631                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19632                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19633                 */
19634                 
19635                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19636                 // r.barchart(30, 30, 560, 250,  xdata, {
19637                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19638                 //     axis : "0 0 1 1",
19639                 //     axisxlabels :  xdata
19640                 //     //yvalues : cols,
19641                    
19642                 // });
19643 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19644 //        
19645 //        this.load(null,xdata,{
19646 //                axis : "0 0 1 1",
19647 //                axisxlabels :  xdata
19648 //                });
19649
19650     },
19651
19652     load : function(graphtype,xdata,opts){
19653         this.raphael.clear();
19654         if(!graphtype) {
19655             graphtype = this.graphtype;
19656         }
19657         if(!opts){
19658             opts = this.opts;
19659         }
19660         var r = this.raphael,
19661             fin = function () {
19662                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19663             },
19664             fout = function () {
19665                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19666             },
19667             pfin = function() {
19668                 this.sector.stop();
19669                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19670
19671                 if (this.label) {
19672                     this.label[0].stop();
19673                     this.label[0].attr({ r: 7.5 });
19674                     this.label[1].attr({ "font-weight": 800 });
19675                 }
19676             },
19677             pfout = function() {
19678                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19679
19680                 if (this.label) {
19681                     this.label[0].animate({ r: 5 }, 500, "bounce");
19682                     this.label[1].attr({ "font-weight": 400 });
19683                 }
19684             };
19685
19686         switch(graphtype){
19687             case 'bar':
19688                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19689                 break;
19690             case 'hbar':
19691                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19692                 break;
19693             case 'pie':
19694 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19695 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19696 //            
19697                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19698                 
19699                 break;
19700
19701         }
19702         
19703         if(this.title){
19704             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19705         }
19706         
19707     },
19708     
19709     setTitle: function(o)
19710     {
19711         this.title = o;
19712     },
19713     
19714     initEvents: function() {
19715         
19716         if(!this.href){
19717             this.el.on('click', this.onClick, this);
19718         }
19719     },
19720     
19721     onClick : function(e)
19722     {
19723         Roo.log('img onclick');
19724         this.fireEvent('click', this, e);
19725     }
19726    
19727 });
19728
19729  
19730 /*
19731  * - LGPL
19732  *
19733  * numberBox
19734  * 
19735  */
19736 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19737
19738 /**
19739  * @class Roo.bootstrap.dash.NumberBox
19740  * @extends Roo.bootstrap.Component
19741  * Bootstrap NumberBox class
19742  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19743  * @cfg {String} headline Box headline
19744  * @cfg {String} content Box content
19745  * @cfg {String} icon Box icon
19746  * @cfg {String} footer Footer text
19747  * @cfg {String} fhref Footer href
19748  * 
19749  * @constructor
19750  * Create a new NumberBox
19751  * @param {Object} config The config object
19752  */
19753
19754
19755 Roo.bootstrap.dash.NumberBox = function(config){
19756     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19757     
19758 };
19759
19760 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19761     
19762     bgcolor : 'aqua',
19763     headline : '',
19764     content : '',
19765     icon : '',
19766     footer : '',
19767     fhref : '',
19768     ficon : '',
19769     
19770     getAutoCreate : function(){
19771         
19772         var cfg = {
19773             tag : 'div',
19774             cls : 'small-box bg-' + this.bgcolor,
19775             cn : [
19776                 {
19777                     tag : 'div',
19778                     cls : 'inner',
19779                     cn :[
19780                         {
19781                             tag : 'h3',
19782                             cls : 'roo-headline',
19783                             html : this.headline
19784                         },
19785                         {
19786                             tag : 'p',
19787                             cls : 'roo-content',
19788                             html : this.content
19789                         }
19790                     ]
19791                 }
19792             ]
19793         }
19794         
19795         if(this.icon){
19796             cfg.cn.push({
19797                 tag : 'div',
19798                 cls : 'icon',
19799                 cn :[
19800                     {
19801                         tag : 'i',
19802                         cls : 'ion ' + this.icon
19803                     }
19804                 ]
19805             });
19806         }
19807         
19808         if(this.footer){
19809             var footer = {
19810                 tag : 'a',
19811                 cls : 'small-box-footer',
19812                 href : this.fhref || '#',
19813                 html : this.footer
19814             };
19815             
19816             cfg.cn.push(footer);
19817             
19818         }
19819         
19820         return  cfg;
19821     },
19822
19823     onRender : function(ct,position){
19824         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19825
19826
19827        
19828                 
19829     },
19830
19831     setHeadline: function (value)
19832     {
19833         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19834     },
19835     
19836     setFooter: function (value, href)
19837     {
19838         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19839         
19840         if(href){
19841             this.el.select('a.small-box-footer',true).first().attr('href', href);
19842         }
19843         
19844     },
19845
19846     setContent: function (value)
19847     {
19848         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19849     },
19850
19851     initEvents: function() 
19852     {   
19853         
19854     }
19855     
19856 });
19857
19858  
19859 /*
19860  * - LGPL
19861  *
19862  * TabBox
19863  * 
19864  */
19865 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19866
19867 /**
19868  * @class Roo.bootstrap.dash.TabBox
19869  * @extends Roo.bootstrap.Component
19870  * Bootstrap TabBox class
19871  * @cfg {String} title Title of the TabBox
19872  * @cfg {String} icon Icon of the TabBox
19873  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19874  * 
19875  * @constructor
19876  * Create a new TabBox
19877  * @param {Object} config The config object
19878  */
19879
19880
19881 Roo.bootstrap.dash.TabBox = function(config){
19882     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19883     this.addEvents({
19884         // raw events
19885         /**
19886          * @event addpane
19887          * When a pane is added
19888          * @param {Roo.bootstrap.dash.TabPane} pane
19889          */
19890         "addpane" : true
19891          
19892     });
19893 };
19894
19895 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19896
19897     title : '',
19898     icon : false,
19899     showtabs : true,
19900     
19901     getChildContainer : function()
19902     {
19903         return this.el.select('.tab-content', true).first();
19904     },
19905     
19906     getAutoCreate : function(){
19907         
19908         var header = {
19909             tag: 'li',
19910             cls: 'pull-left header',
19911             html: this.title,
19912             cn : []
19913         };
19914         
19915         if(this.icon){
19916             header.cn.push({
19917                 tag: 'i',
19918                 cls: 'fa ' + this.icon
19919             });
19920         }
19921         
19922         
19923         var cfg = {
19924             tag: 'div',
19925             cls: 'nav-tabs-custom',
19926             cn: [
19927                 {
19928                     tag: 'ul',
19929                     cls: 'nav nav-tabs pull-right',
19930                     cn: [
19931                         header
19932                     ]
19933                 },
19934                 {
19935                     tag: 'div',
19936                     cls: 'tab-content no-padding',
19937                     cn: []
19938                 }
19939             ]
19940         }
19941
19942         return  cfg;
19943     },
19944     initEvents : function()
19945     {
19946         //Roo.log('add add pane handler');
19947         this.on('addpane', this.onAddPane, this);
19948     },
19949      /**
19950      * Updates the box title
19951      * @param {String} html to set the title to.
19952      */
19953     setTitle : function(value)
19954     {
19955         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19956     },
19957     onAddPane : function(pane)
19958     {
19959         //Roo.log('addpane');
19960         //Roo.log(pane);
19961         // tabs are rendere left to right..
19962         if(!this.showtabs){
19963             return;
19964         }
19965         
19966         var ctr = this.el.select('.nav-tabs', true).first();
19967          
19968          
19969         var existing = ctr.select('.nav-tab',true);
19970         var qty = existing.getCount();;
19971         
19972         
19973         var tab = ctr.createChild({
19974             tag : 'li',
19975             cls : 'nav-tab' + (qty ? '' : ' active'),
19976             cn : [
19977                 {
19978                     tag : 'a',
19979                     href:'#',
19980                     html : pane.title
19981                 }
19982             ]
19983         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19984         pane.tab = tab;
19985         
19986         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19987         if (!qty) {
19988             pane.el.addClass('active');
19989         }
19990         
19991                 
19992     },
19993     onTabClick : function(ev,un,ob,pane)
19994     {
19995         //Roo.log('tab - prev default');
19996         ev.preventDefault();
19997         
19998         
19999         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20000         pane.tab.addClass('active');
20001         //Roo.log(pane.title);
20002         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20003         // technically we should have a deactivate event.. but maybe add later.
20004         // and it should not de-activate the selected tab...
20005         
20006         pane.el.addClass('active');
20007         pane.fireEvent('activate');
20008         
20009         
20010     }
20011     
20012     
20013 });
20014
20015  
20016 /*
20017  * - LGPL
20018  *
20019  * Tab pane
20020  * 
20021  */
20022 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20023 /**
20024  * @class Roo.bootstrap.TabPane
20025  * @extends Roo.bootstrap.Component
20026  * Bootstrap TabPane class
20027  * @cfg {Boolean} active (false | true) Default false
20028  * @cfg {String} title title of panel
20029
20030  * 
20031  * @constructor
20032  * Create a new TabPane
20033  * @param {Object} config The config object
20034  */
20035
20036 Roo.bootstrap.dash.TabPane = function(config){
20037     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20038     
20039     this.addEvents({
20040         // raw events
20041         /**
20042          * @event activate
20043          * When a pane is activated
20044          * @param {Roo.bootstrap.dash.TabPane} pane
20045          */
20046         "activate" : true
20047          
20048     });
20049 };
20050
20051 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20052     
20053     active : false,
20054     title : '',
20055     
20056     // the tabBox that this is attached to.
20057     tab : false,
20058      
20059     getAutoCreate : function() 
20060     {
20061         var cfg = {
20062             tag: 'div',
20063             cls: 'tab-pane'
20064         }
20065         
20066         if(this.active){
20067             cfg.cls += ' active';
20068         }
20069         
20070         return cfg;
20071     },
20072     initEvents  : function()
20073     {
20074         //Roo.log('trigger add pane handler');
20075         this.parent().fireEvent('addpane', this)
20076     },
20077     
20078      /**
20079      * Updates the tab title 
20080      * @param {String} html to set the title to.
20081      */
20082     setTitle: function(str)
20083     {
20084         if (!this.tab) {
20085             return;
20086         }
20087         this.title = str;
20088         this.tab.select('a', true).first().dom.innerHTML = str;
20089         
20090     }
20091     
20092     
20093     
20094 });
20095
20096  
20097
20098
20099  /*
20100  * - LGPL
20101  *
20102  * menu
20103  * 
20104  */
20105 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20106
20107 /**
20108  * @class Roo.bootstrap.menu.Menu
20109  * @extends Roo.bootstrap.Component
20110  * Bootstrap Menu class - container for Menu
20111  * @cfg {String} html Text of the menu
20112  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20113  * @cfg {String} icon Font awesome icon
20114  * @cfg {String} pos Menu align to (top | bottom) default bottom
20115  * 
20116  * 
20117  * @constructor
20118  * Create a new Menu
20119  * @param {Object} config The config object
20120  */
20121
20122
20123 Roo.bootstrap.menu.Menu = function(config){
20124     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20125     
20126     this.addEvents({
20127         /**
20128          * @event beforeshow
20129          * Fires before this menu is displayed
20130          * @param {Roo.bootstrap.menu.Menu} this
20131          */
20132         beforeshow : true,
20133         /**
20134          * @event beforehide
20135          * Fires before this menu is hidden
20136          * @param {Roo.bootstrap.menu.Menu} this
20137          */
20138         beforehide : true,
20139         /**
20140          * @event show
20141          * Fires after this menu is displayed
20142          * @param {Roo.bootstrap.menu.Menu} this
20143          */
20144         show : true,
20145         /**
20146          * @event hide
20147          * Fires after this menu is hidden
20148          * @param {Roo.bootstrap.menu.Menu} this
20149          */
20150         hide : true,
20151         /**
20152          * @event click
20153          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20154          * @param {Roo.bootstrap.menu.Menu} this
20155          * @param {Roo.EventObject} e
20156          */
20157         click : true
20158     });
20159     
20160 };
20161
20162 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20163     
20164     submenu : false,
20165     html : '',
20166     weight : 'default',
20167     icon : false,
20168     pos : 'bottom',
20169     
20170     
20171     getChildContainer : function() {
20172         if(this.isSubMenu){
20173             return this.el;
20174         }
20175         
20176         return this.el.select('ul.dropdown-menu', true).first();  
20177     },
20178     
20179     getAutoCreate : function()
20180     {
20181         var text = [
20182             {
20183                 tag : 'span',
20184                 cls : 'roo-menu-text',
20185                 html : this.html
20186             }
20187         ];
20188         
20189         if(this.icon){
20190             text.unshift({
20191                 tag : 'i',
20192                 cls : 'fa ' + this.icon
20193             })
20194         }
20195         
20196         
20197         var cfg = {
20198             tag : 'div',
20199             cls : 'btn-group',
20200             cn : [
20201                 {
20202                     tag : 'button',
20203                     cls : 'dropdown-button btn btn-' + this.weight,
20204                     cn : text
20205                 },
20206                 {
20207                     tag : 'button',
20208                     cls : 'dropdown-toggle btn btn-' + this.weight,
20209                     cn : [
20210                         {
20211                             tag : 'span',
20212                             cls : 'caret'
20213                         }
20214                     ]
20215                 },
20216                 {
20217                     tag : 'ul',
20218                     cls : 'dropdown-menu'
20219                 }
20220             ]
20221             
20222         };
20223         
20224         if(this.pos == 'top'){
20225             cfg.cls += ' dropup';
20226         }
20227         
20228         if(this.isSubMenu){
20229             cfg = {
20230                 tag : 'ul',
20231                 cls : 'dropdown-menu'
20232             }
20233         }
20234         
20235         return cfg;
20236     },
20237     
20238     onRender : function(ct, position)
20239     {
20240         this.isSubMenu = ct.hasClass('dropdown-submenu');
20241         
20242         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20243     },
20244     
20245     initEvents : function() 
20246     {
20247         if(this.isSubMenu){
20248             return;
20249         }
20250         
20251         this.hidden = true;
20252         
20253         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20254         this.triggerEl.on('click', this.onTriggerPress, this);
20255         
20256         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20257         this.buttonEl.on('click', this.onClick, this);
20258         
20259     },
20260     
20261     list : function()
20262     {
20263         if(this.isSubMenu){
20264             return this.el;
20265         }
20266         
20267         return this.el.select('ul.dropdown-menu', true).first();
20268     },
20269     
20270     onClick : function(e)
20271     {
20272         this.fireEvent("click", this, e);
20273     },
20274     
20275     onTriggerPress  : function(e)
20276     {   
20277         if (this.isVisible()) {
20278             this.hide();
20279         } else {
20280             this.show();
20281         }
20282     },
20283     
20284     isVisible : function(){
20285         return !this.hidden;
20286     },
20287     
20288     show : function()
20289     {
20290         this.fireEvent("beforeshow", this);
20291         
20292         this.hidden = false;
20293         this.el.addClass('open');
20294         
20295         Roo.get(document).on("mouseup", this.onMouseUp, this);
20296         
20297         this.fireEvent("show", this);
20298         
20299         
20300     },
20301     
20302     hide : function()
20303     {
20304         this.fireEvent("beforehide", this);
20305         
20306         this.hidden = true;
20307         this.el.removeClass('open');
20308         
20309         Roo.get(document).un("mouseup", this.onMouseUp);
20310         
20311         this.fireEvent("hide", this);
20312     },
20313     
20314     onMouseUp : function()
20315     {
20316         this.hide();
20317     }
20318     
20319 });
20320
20321  
20322  /*
20323  * - LGPL
20324  *
20325  * menu item
20326  * 
20327  */
20328 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20329
20330 /**
20331  * @class Roo.bootstrap.menu.Item
20332  * @extends Roo.bootstrap.Component
20333  * Bootstrap MenuItem class
20334  * @cfg {Boolean} submenu (true | false) default false
20335  * @cfg {String} html text of the item
20336  * @cfg {String} href the link
20337  * @cfg {Boolean} disable (true | false) default false
20338  * @cfg {Boolean} preventDefault (true | false) default true
20339  * @cfg {String} icon Font awesome icon
20340  * @cfg {String} pos Submenu align to (left | right) default right 
20341  * 
20342  * 
20343  * @constructor
20344  * Create a new Item
20345  * @param {Object} config The config object
20346  */
20347
20348
20349 Roo.bootstrap.menu.Item = function(config){
20350     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20351     this.addEvents({
20352         /**
20353          * @event mouseover
20354          * Fires when the mouse is hovering over this menu
20355          * @param {Roo.bootstrap.menu.Item} this
20356          * @param {Roo.EventObject} e
20357          */
20358         mouseover : true,
20359         /**
20360          * @event mouseout
20361          * Fires when the mouse exits this menu
20362          * @param {Roo.bootstrap.menu.Item} this
20363          * @param {Roo.EventObject} e
20364          */
20365         mouseout : true,
20366         // raw events
20367         /**
20368          * @event click
20369          * The raw click event for the entire grid.
20370          * @param {Roo.EventObject} e
20371          */
20372         click : true
20373     });
20374 };
20375
20376 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20377     
20378     submenu : false,
20379     href : '',
20380     html : '',
20381     preventDefault: true,
20382     disable : false,
20383     icon : false,
20384     pos : 'right',
20385     
20386     getAutoCreate : function()
20387     {
20388         var text = [
20389             {
20390                 tag : 'span',
20391                 cls : 'roo-menu-item-text',
20392                 html : this.html
20393             }
20394         ];
20395         
20396         if(this.icon){
20397             text.unshift({
20398                 tag : 'i',
20399                 cls : 'fa ' + this.icon
20400             })
20401         }
20402         
20403         var cfg = {
20404             tag : 'li',
20405             cn : [
20406                 {
20407                     tag : 'a',
20408                     href : this.href || '#',
20409                     cn : text
20410                 }
20411             ]
20412         };
20413         
20414         if(this.disable){
20415             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20416         }
20417         
20418         if(this.submenu){
20419             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20420             
20421             if(this.pos == 'left'){
20422                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20423             }
20424         }
20425         
20426         return cfg;
20427     },
20428     
20429     initEvents : function() 
20430     {
20431         this.el.on('mouseover', this.onMouseOver, this);
20432         this.el.on('mouseout', this.onMouseOut, this);
20433         
20434         this.el.select('a', true).first().on('click', this.onClick, this);
20435         
20436     },
20437     
20438     onClick : function(e)
20439     {
20440         if(this.preventDefault){
20441             e.preventDefault();
20442         }
20443         
20444         this.fireEvent("click", this, e);
20445     },
20446     
20447     onMouseOver : function(e)
20448     {
20449         if(this.submenu && this.pos == 'left'){
20450             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20451         }
20452         
20453         this.fireEvent("mouseover", this, e);
20454     },
20455     
20456     onMouseOut : function(e)
20457     {
20458         this.fireEvent("mouseout", this, e);
20459     }
20460 });
20461
20462  
20463
20464  /*
20465  * - LGPL
20466  *
20467  * menu separator
20468  * 
20469  */
20470 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20471
20472 /**
20473  * @class Roo.bootstrap.menu.Separator
20474  * @extends Roo.bootstrap.Component
20475  * Bootstrap Separator class
20476  * 
20477  * @constructor
20478  * Create a new Separator
20479  * @param {Object} config The config object
20480  */
20481
20482
20483 Roo.bootstrap.menu.Separator = function(config){
20484     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20485 };
20486
20487 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20488     
20489     getAutoCreate : function(){
20490         var cfg = {
20491             tag : 'li',
20492             cls: 'divider'
20493         };
20494         
20495         return cfg;
20496     }
20497    
20498 });
20499
20500  
20501
20502