docs/default.css
[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")){
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){
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         Roo.log('pass click event');
1714         
1715         t.onClick(e);
1716         
1717         this.fireEvent("click", this, t, e);
1718         
1719         this.hide();
1720     },
1721      onMouseOver : function(e){
1722         var t  = this.findTargetItem(e);
1723         //Roo.log(t);
1724         //if(t){
1725         //    if(t.canActivate && !t.disabled){
1726         //        this.setActiveItem(t, true);
1727         //    }
1728         //}
1729         
1730         this.fireEvent("mouseover", this, e, t);
1731     },
1732     isVisible : function(){
1733         return !this.hidden;
1734     },
1735      onMouseOut : function(e){
1736         var t  = this.findTargetItem(e);
1737         
1738         //if(t ){
1739         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1740         //        this.activeItem.deactivate();
1741         //        delete this.activeItem;
1742         //    }
1743         //}
1744         this.fireEvent("mouseout", this, e, t);
1745     },
1746     
1747     
1748     /**
1749      * Displays this menu relative to another element
1750      * @param {String/HTMLElement/Roo.Element} element The element to align to
1751      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1752      * the element (defaults to this.defaultAlign)
1753      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1754      */
1755     show : function(el, pos, parentMenu){
1756         this.parentMenu = parentMenu;
1757         if(!this.el){
1758             this.render();
1759         }
1760         this.fireEvent("beforeshow", this);
1761         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1762     },
1763      /**
1764      * Displays this menu at a specific xy position
1765      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1766      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1767      */
1768     showAt : function(xy, parentMenu, /* private: */_e){
1769         this.parentMenu = parentMenu;
1770         if(!this.el){
1771             this.render();
1772         }
1773         if(_e !== false){
1774             this.fireEvent("beforeshow", this);
1775             
1776             //xy = this.el.adjustForConstraints(xy);
1777         }
1778         //this.el.setXY(xy);
1779         //this.el.show();
1780         this.hideMenuItems();
1781         this.hidden = false;
1782         this.triggerEl.addClass('open');
1783         this.focus();
1784         this.fireEvent("show", this);
1785     },
1786     
1787     focus : function(){
1788         return;
1789         if(!this.hidden){
1790             this.doFocus.defer(50, this);
1791         }
1792     },
1793
1794     doFocus : function(){
1795         if(!this.hidden){
1796             this.focusEl.focus();
1797         }
1798     },
1799
1800     /**
1801      * Hides this menu and optionally all parent menus
1802      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1803      */
1804     hide : function(deep){
1805         
1806         this.hideMenuItems();
1807         if(this.el && this.isVisible()){
1808             this.fireEvent("beforehide", this);
1809             if(this.activeItem){
1810                 this.activeItem.deactivate();
1811                 this.activeItem = null;
1812             }
1813             this.triggerEl.removeClass('open');;
1814             this.hidden = true;
1815             this.fireEvent("hide", this);
1816         }
1817         if(deep === true && this.parentMenu){
1818             this.parentMenu.hide(true);
1819         }
1820     },
1821     
1822     onTriggerPress  : function(e)
1823     {
1824         
1825         Roo.log('trigger press');
1826         //Roo.log(e.getTarget());
1827        // Roo.log(this.triggerEl.dom);
1828         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1829             return;
1830         }
1831         if (this.isVisible()) {
1832             Roo.log('hide');
1833             this.hide();
1834         } else {
1835             this.show(this.triggerEl, false, false);
1836         }
1837         
1838         
1839     },
1840     
1841          
1842        
1843     
1844     hideMenuItems : function()
1845     {
1846         //$(backdrop).remove()
1847         Roo.select('.open',true).each(function(aa) {
1848             
1849             aa.removeClass('open');
1850           //var parent = getParent($(this))
1851           //var relatedTarget = { relatedTarget: this }
1852           
1853            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1854           //if (e.isDefaultPrevented()) return
1855            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1856         })
1857     },
1858     addxtypeChild : function (tree, cntr) {
1859         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1860           
1861         this.menuitems.add(comp);
1862         return comp;
1863
1864     },
1865     getEl : function()
1866     {
1867         Roo.log(this.el);
1868         return this.el;
1869     }
1870 });
1871
1872  
1873  /*
1874  * - LGPL
1875  *
1876  * menu item
1877  * 
1878  */
1879
1880
1881 /**
1882  * @class Roo.bootstrap.MenuItem
1883  * @extends Roo.bootstrap.Component
1884  * Bootstrap MenuItem class
1885  * @cfg {String} html the menu label
1886  * @cfg {String} href the link
1887  * @cfg {Boolean} preventDefault (true | false) default true
1888  * @cfg {Boolean} isContainer (true | false) default false
1889  * 
1890  * 
1891  * @constructor
1892  * Create a new MenuItem
1893  * @param {Object} config The config object
1894  */
1895
1896
1897 Roo.bootstrap.MenuItem = function(config){
1898     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1899     this.addEvents({
1900         // raw events
1901         /**
1902          * @event click
1903          * The raw click event for the entire grid.
1904          * @param {Roo.EventObject} e
1905          */
1906         "click" : true
1907     });
1908 };
1909
1910 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1911     
1912     href : false,
1913     html : false,
1914     preventDefault: true,
1915     isContainer : false,
1916     
1917     getAutoCreate : function(){
1918         
1919         if(this.isContainer){
1920             return {
1921                 tag: 'li',
1922                 cls: 'dropdown-menu-item'
1923             };
1924         }
1925         
1926         var cfg= {
1927             tag: 'li',
1928             cls: 'dropdown-menu-item',
1929             cn: [
1930                     {
1931                         tag : 'a',
1932                         href : '#',
1933                         html : 'Link'
1934                     }
1935                 ]
1936         };
1937         if (this.parent().type == 'treeview') {
1938             cfg.cls = 'treeview-menu';
1939         }
1940         
1941         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1942         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1943         return cfg;
1944     },
1945     
1946     initEvents: function() {
1947         
1948         //this.el.select('a').on('click', this.onClick, this);
1949         
1950     },
1951     onClick : function(e)
1952     {
1953         Roo.log('item on click ');
1954         //if(this.preventDefault){
1955         //    e.preventDefault();
1956         //}
1957         //this.parent().hideMenuItems();
1958         
1959         this.fireEvent('click', this, e);
1960     },
1961     getEl : function()
1962     {
1963         return this.el;
1964     }
1965 });
1966
1967  
1968
1969  /*
1970  * - LGPL
1971  *
1972  * menu separator
1973  * 
1974  */
1975
1976
1977 /**
1978  * @class Roo.bootstrap.MenuSeparator
1979  * @extends Roo.bootstrap.Component
1980  * Bootstrap MenuSeparator class
1981  * 
1982  * @constructor
1983  * Create a new MenuItem
1984  * @param {Object} config The config object
1985  */
1986
1987
1988 Roo.bootstrap.MenuSeparator = function(config){
1989     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1990 };
1991
1992 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1993     
1994     getAutoCreate : function(){
1995         var cfg = {
1996             cls: 'divider',
1997             tag : 'li'
1998         };
1999         
2000         return cfg;
2001     }
2002    
2003 });
2004
2005  
2006
2007  
2008 /*
2009 <div class="modal fade">
2010   <div class="modal-dialog">
2011     <div class="modal-content">
2012       <div class="modal-header">
2013         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2014         <h4 class="modal-title">Modal title</h4>
2015       </div>
2016       <div class="modal-body">
2017         <p>One fine body&hellip;</p>
2018       </div>
2019       <div class="modal-footer">
2020         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2021         <button type="button" class="btn btn-primary">Save changes</button>
2022       </div>
2023     </div><!-- /.modal-content -->
2024   </div><!-- /.modal-dialog -->
2025 </div><!-- /.modal -->
2026 */
2027 /*
2028  * - LGPL
2029  *
2030  * page contgainer.
2031  * 
2032  */
2033
2034 /**
2035  * @class Roo.bootstrap.Modal
2036  * @extends Roo.bootstrap.Component
2037  * Bootstrap Modal class
2038  * @cfg {String} title Title of dialog
2039  * @cfg {Boolean} specificTitle (true|false) default false
2040  * @cfg {Array} buttons Array of buttons or standard button set..
2041  * @cfg {String} buttonPosition (left|right|center) default right
2042  * @cfg {Boolean} animate (true | false) default true
2043  * 
2044  * @constructor
2045  * Create a new Modal Dialog
2046  * @param {Object} config The config object
2047  */
2048
2049 Roo.bootstrap.Modal = function(config){
2050     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2051     this.addEvents({
2052         // raw events
2053         /**
2054          * @event btnclick
2055          * The raw btnclick event for the button
2056          * @param {Roo.EventObject} e
2057          */
2058         "btnclick" : true
2059     });
2060     this.buttons = this.buttons || [];
2061 };
2062
2063 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2064     
2065     title : 'test dialog',
2066    
2067     buttons : false,
2068     
2069     // set on load...
2070     body:  false,
2071     
2072     specificTitle: false,
2073     
2074     buttonPosition: 'right',
2075     
2076     animate : true,
2077     
2078     onRender : function(ct, position)
2079     {
2080         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2081      
2082         if(!this.el){
2083             var cfg = Roo.apply({},  this.getAutoCreate());
2084             cfg.id = Roo.id();
2085             //if(!cfg.name){
2086             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2087             //}
2088             //if (!cfg.name.length) {
2089             //    delete cfg.name;
2090            // }
2091             if (this.cls) {
2092                 cfg.cls += ' ' + this.cls;
2093             }
2094             if (this.style) {
2095                 cfg.style = this.style;
2096             }
2097             this.el = Roo.get(document.body).createChild(cfg, position);
2098         }
2099         //var type = this.el.dom.type;
2100         
2101         if(this.tabIndex !== undefined){
2102             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2103         }
2104         
2105         
2106         
2107         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2108         this.maskEl.enableDisplayMode("block");
2109         this.maskEl.hide();
2110         //this.el.addClass("x-dlg-modal");
2111     
2112         if (this.buttons.length) {
2113             Roo.each(this.buttons, function(bb) {
2114                 b = Roo.apply({}, bb);
2115                 b.xns = b.xns || Roo.bootstrap;
2116                 b.xtype = b.xtype || 'Button';
2117                 if (typeof(b.listeners) == 'undefined') {
2118                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2119                 }
2120                 
2121                 var btn = Roo.factory(b);
2122                 
2123                 btn.onRender(this.el.select('.modal-footer div').first());
2124                 
2125             },this);
2126         }
2127         // render the children.
2128         var nitems = [];
2129         
2130         if(typeof(this.items) != 'undefined'){
2131             var items = this.items;
2132             delete this.items;
2133
2134             for(var i =0;i < items.length;i++) {
2135                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2136             }
2137         }
2138         
2139         this.items = nitems;
2140         
2141         this.body = this.el.select('.modal-body',true).first();
2142         this.close = this.el.select('.modal-header .close', true).first();
2143         this.footer = this.el.select('.modal-footer',true).first();
2144         this.initEvents();
2145         //this.el.addClass([this.fieldClass, this.cls]);
2146         
2147     },
2148     getAutoCreate : function(){
2149         
2150         
2151         var bdy = {
2152                 cls : 'modal-body',
2153                 html : this.html || ''
2154         };
2155         
2156         var title = {
2157             tag: 'h4',
2158             cls : 'modal-title',
2159             html : this.title
2160         };
2161         
2162         if(this.specificTitle){
2163             title = this.title;
2164         };
2165         
2166         var modal = {
2167             cls: "modal",
2168             style : 'display: none',
2169             cn : [
2170                 {
2171                     cls: "modal-dialog",
2172                     cn : [
2173                         {
2174                             cls : "modal-content",
2175                             cn : [
2176                                 {
2177                                     cls : 'modal-header',
2178                                     cn : [
2179                                         {
2180                                             tag: 'button',
2181                                             cls : 'close',
2182                                             html : '&times'
2183                                         },
2184                                         title
2185                                     ]
2186                                 },
2187                                 bdy,
2188                                 {
2189                                     cls : 'modal-footer',
2190                                     cn : [
2191                                         {
2192                                             tag: 'div',
2193                                             cls: 'btn-' + this.buttonPosition
2194                                         }
2195                                     ]
2196                                     
2197                                 }
2198                                 
2199                                 
2200                             ]
2201                             
2202                         }
2203                     ]
2204                         
2205                 }
2206             ]
2207         };
2208         
2209         if(this.animate){
2210             modal.cls += ' fade';
2211         }
2212         
2213         return modal;
2214           
2215     },
2216     getChildContainer : function() {
2217          
2218          return this.el.select('.modal-body',true).first();
2219         
2220     },
2221     getButtonContainer : function() {
2222          return this.el.select('.modal-footer div',true).first();
2223         
2224     },
2225     initEvents : function()
2226     {
2227         this.el.select('.modal-header .close').on('click', this.hide, this);
2228 //        
2229 //        this.addxtype(this);
2230     },
2231     show : function() {
2232         
2233         if (!this.rendered) {
2234             this.render();
2235         }
2236         
2237         this.el.setStyle('display', 'block');
2238         
2239         if(this.animate){
2240             var _this = this;
2241             (function(){ _this.el.addClass('in'); }).defer(50);
2242         }else{
2243             this.el.addClass('in');
2244         }
2245         
2246         Roo.get(document.body).addClass("x-body-masked");
2247         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2248         this.maskEl.show();
2249         this.el.setStyle('zIndex', '10001');
2250         this.fireEvent('show', this);
2251         
2252         
2253     },
2254     hide : function()
2255     {
2256         this.maskEl.hide();
2257         Roo.get(document.body).removeClass("x-body-masked");
2258         this.el.removeClass('in');
2259         
2260         if(this.animate){
2261             var _this = this;
2262             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2263         }else{
2264             this.el.setStyle('display', 'none');
2265         }
2266         
2267         this.fireEvent('hide', this);
2268     },
2269     
2270     addButton : function(str, cb)
2271     {
2272          
2273         
2274         var b = Roo.apply({}, { html : str } );
2275         b.xns = b.xns || Roo.bootstrap;
2276         b.xtype = b.xtype || 'Button';
2277         if (typeof(b.listeners) == 'undefined') {
2278             b.listeners = { click : cb.createDelegate(this)  };
2279         }
2280         
2281         var btn = Roo.factory(b);
2282            
2283         btn.onRender(this.el.select('.modal-footer div').first());
2284         
2285         return btn;   
2286        
2287     },
2288     
2289     setDefaultButton : function(btn)
2290     {
2291         //this.el.select('.modal-footer').()
2292     },
2293     resizeTo: function(w,h)
2294     {
2295         // skip..
2296     },
2297     setContentSize  : function(w, h)
2298     {
2299         
2300     },
2301     onButtonClick: function(btn,e)
2302     {
2303         //Roo.log([a,b,c]);
2304         this.fireEvent('btnclick', btn.name, e);
2305     },
2306     setTitle: function(str) {
2307         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2308         
2309     }
2310 });
2311
2312
2313 Roo.apply(Roo.bootstrap.Modal,  {
2314     /**
2315          * Button config that displays a single OK button
2316          * @type Object
2317          */
2318         OK :  [{
2319             name : 'ok',
2320             weight : 'primary',
2321             html : 'OK'
2322         }], 
2323         /**
2324          * Button config that displays Yes and No buttons
2325          * @type Object
2326          */
2327         YESNO : [
2328             {
2329                 name  : 'no',
2330                 html : 'No'
2331             },
2332             {
2333                 name  :'yes',
2334                 weight : 'primary',
2335                 html : 'Yes'
2336             }
2337         ],
2338         
2339         /**
2340          * Button config that displays OK and Cancel buttons
2341          * @type Object
2342          */
2343         OKCANCEL : [
2344             {
2345                name : 'cancel',
2346                 html : 'Cancel'
2347             },
2348             {
2349                 name : 'ok',
2350                 weight : 'primary',
2351                 html : 'OK'
2352             }
2353         ],
2354         /**
2355          * Button config that displays Yes, No and Cancel buttons
2356          * @type Object
2357          */
2358         YESNOCANCEL : [
2359             {
2360                 name : 'yes',
2361                 weight : 'primary',
2362                 html : 'Yes'
2363             },
2364             {
2365                 name : 'no',
2366                 html : 'No'
2367             },
2368             {
2369                 name : 'cancel',
2370                 html : 'Cancel'
2371             }
2372         ]
2373 });
2374  /*
2375  * - LGPL
2376  *
2377  * messagebox - can be used as a replace
2378  * 
2379  */
2380 /**
2381  * @class Roo.MessageBox
2382  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2383  * Example usage:
2384  *<pre><code>
2385 // Basic alert:
2386 Roo.Msg.alert('Status', 'Changes saved successfully.');
2387
2388 // Prompt for user data:
2389 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2390     if (btn == 'ok'){
2391         // process text value...
2392     }
2393 });
2394
2395 // Show a dialog using config options:
2396 Roo.Msg.show({
2397    title:'Save Changes?',
2398    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2399    buttons: Roo.Msg.YESNOCANCEL,
2400    fn: processResult,
2401    animEl: 'elId'
2402 });
2403 </code></pre>
2404  * @singleton
2405  */
2406 Roo.bootstrap.MessageBox = function(){
2407     var dlg, opt, mask, waitTimer;
2408     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2409     var buttons, activeTextEl, bwidth;
2410
2411     
2412     // private
2413     var handleButton = function(button){
2414         dlg.hide();
2415         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2416     };
2417
2418     // private
2419     var handleHide = function(){
2420         if(opt && opt.cls){
2421             dlg.el.removeClass(opt.cls);
2422         }
2423         //if(waitTimer){
2424         //    Roo.TaskMgr.stop(waitTimer);
2425         //    waitTimer = null;
2426         //}
2427     };
2428
2429     // private
2430     var updateButtons = function(b){
2431         var width = 0;
2432         if(!b){
2433             buttons["ok"].hide();
2434             buttons["cancel"].hide();
2435             buttons["yes"].hide();
2436             buttons["no"].hide();
2437             //dlg.footer.dom.style.display = 'none';
2438             return width;
2439         }
2440         dlg.footer.dom.style.display = '';
2441         for(var k in buttons){
2442             if(typeof buttons[k] != "function"){
2443                 if(b[k]){
2444                     buttons[k].show();
2445                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2446                     width += buttons[k].el.getWidth()+15;
2447                 }else{
2448                     buttons[k].hide();
2449                 }
2450             }
2451         }
2452         return width;
2453     };
2454
2455     // private
2456     var handleEsc = function(d, k, e){
2457         if(opt && opt.closable !== false){
2458             dlg.hide();
2459         }
2460         if(e){
2461             e.stopEvent();
2462         }
2463     };
2464
2465     return {
2466         /**
2467          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2468          * @return {Roo.BasicDialog} The BasicDialog element
2469          */
2470         getDialog : function(){
2471            if(!dlg){
2472                 dlg = new Roo.bootstrap.Modal( {
2473                     //draggable: true,
2474                     //resizable:false,
2475                     //constraintoviewport:false,
2476                     //fixedcenter:true,
2477                     //collapsible : false,
2478                     //shim:true,
2479                     //modal: true,
2480                   //  width:400,
2481                   //  height:100,
2482                     //buttonAlign:"center",
2483                     closeClick : function(){
2484                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2485                             handleButton("no");
2486                         }else{
2487                             handleButton("cancel");
2488                         }
2489                     }
2490                 });
2491                 dlg.render();
2492                 dlg.on("hide", handleHide);
2493                 mask = dlg.mask;
2494                 //dlg.addKeyListener(27, handleEsc);
2495                 buttons = {};
2496                 this.buttons = buttons;
2497                 var bt = this.buttonText;
2498                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2499                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2500                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2501                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2502                 Roo.log(buttons)
2503                 bodyEl = dlg.body.createChild({
2504
2505                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2506                         '<textarea class="roo-mb-textarea"></textarea>' +
2507                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2508                 });
2509                 msgEl = bodyEl.dom.firstChild;
2510                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2511                 textboxEl.enableDisplayMode();
2512                 textboxEl.addKeyListener([10,13], function(){
2513                     if(dlg.isVisible() && opt && opt.buttons){
2514                         if(opt.buttons.ok){
2515                             handleButton("ok");
2516                         }else if(opt.buttons.yes){
2517                             handleButton("yes");
2518                         }
2519                     }
2520                 });
2521                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2522                 textareaEl.enableDisplayMode();
2523                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2524                 progressEl.enableDisplayMode();
2525                 var pf = progressEl.dom.firstChild;
2526                 if (pf) {
2527                     pp = Roo.get(pf.firstChild);
2528                     pp.setHeight(pf.offsetHeight);
2529                 }
2530                 
2531             }
2532             return dlg;
2533         },
2534
2535         /**
2536          * Updates the message box body text
2537          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2538          * the XHTML-compliant non-breaking space character '&amp;#160;')
2539          * @return {Roo.MessageBox} This message box
2540          */
2541         updateText : function(text){
2542             if(!dlg.isVisible() && !opt.width){
2543                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2544             }
2545             msgEl.innerHTML = text || '&#160;';
2546       
2547             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2548             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2549             var w = Math.max(
2550                     Math.min(opt.width || cw , this.maxWidth), 
2551                     Math.max(opt.minWidth || this.minWidth, bwidth)
2552             );
2553             if(opt.prompt){
2554                 activeTextEl.setWidth(w);
2555             }
2556             if(dlg.isVisible()){
2557                 dlg.fixedcenter = false;
2558             }
2559             // to big, make it scroll. = But as usual stupid IE does not support
2560             // !important..
2561             
2562             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2563                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2564                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2565             } else {
2566                 bodyEl.dom.style.height = '';
2567                 bodyEl.dom.style.overflowY = '';
2568             }
2569             if (cw > w) {
2570                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2571             } else {
2572                 bodyEl.dom.style.overflowX = '';
2573             }
2574             
2575             dlg.setContentSize(w, bodyEl.getHeight());
2576             if(dlg.isVisible()){
2577                 dlg.fixedcenter = true;
2578             }
2579             return this;
2580         },
2581
2582         /**
2583          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2584          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2585          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2586          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2587          * @return {Roo.MessageBox} This message box
2588          */
2589         updateProgress : function(value, text){
2590             if(text){
2591                 this.updateText(text);
2592             }
2593             if (pp) { // weird bug on my firefox - for some reason this is not defined
2594                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2595             }
2596             return this;
2597         },        
2598
2599         /**
2600          * Returns true if the message box is currently displayed
2601          * @return {Boolean} True if the message box is visible, else false
2602          */
2603         isVisible : function(){
2604             return dlg && dlg.isVisible();  
2605         },
2606
2607         /**
2608          * Hides the message box if it is displayed
2609          */
2610         hide : function(){
2611             if(this.isVisible()){
2612                 dlg.hide();
2613             }  
2614         },
2615
2616         /**
2617          * Displays a new message box, or reinitializes an existing message box, based on the config options
2618          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2619          * The following config object properties are supported:
2620          * <pre>
2621 Property    Type             Description
2622 ----------  ---------------  ------------------------------------------------------------------------------------
2623 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2624                                    closes (defaults to undefined)
2625 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2626                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2627 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2628                                    progress and wait dialogs will ignore this property and always hide the
2629                                    close button as they can only be closed programmatically.
2630 cls               String           A custom CSS class to apply to the message box element
2631 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2632                                    displayed (defaults to 75)
2633 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2634                                    function will be btn (the name of the button that was clicked, if applicable,
2635                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2636                                    Progress and wait dialogs will ignore this option since they do not respond to
2637                                    user actions and can only be closed programmatically, so any required function
2638                                    should be called by the same code after it closes the dialog.
2639 icon              String           A CSS class that provides a background image to be used as an icon for
2640                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2641 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2642 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2643 modal             Boolean          False to allow user interaction with the page while the message box is
2644                                    displayed (defaults to true)
2645 msg               String           A string that will replace the existing message box body text (defaults
2646                                    to the XHTML-compliant non-breaking space character '&#160;')
2647 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2648 progress          Boolean          True to display a progress bar (defaults to false)
2649 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2650 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2651 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2652 title             String           The title text
2653 value             String           The string value to set into the active textbox element if displayed
2654 wait              Boolean          True to display a progress bar (defaults to false)
2655 width             Number           The width of the dialog in pixels
2656 </pre>
2657          *
2658          * Example usage:
2659          * <pre><code>
2660 Roo.Msg.show({
2661    title: 'Address',
2662    msg: 'Please enter your address:',
2663    width: 300,
2664    buttons: Roo.MessageBox.OKCANCEL,
2665    multiline: true,
2666    fn: saveAddress,
2667    animEl: 'addAddressBtn'
2668 });
2669 </code></pre>
2670          * @param {Object} config Configuration options
2671          * @return {Roo.MessageBox} This message box
2672          */
2673         show : function(options)
2674         {
2675             
2676             // this causes nightmares if you show one dialog after another
2677             // especially on callbacks..
2678              
2679             if(this.isVisible()){
2680                 
2681                 this.hide();
2682                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2683                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2684                 Roo.log("New Dialog Message:" +  options.msg )
2685                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2686                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2687                 
2688             }
2689             var d = this.getDialog();
2690             opt = options;
2691             d.setTitle(opt.title || "&#160;");
2692             d.close.setDisplayed(opt.closable !== false);
2693             activeTextEl = textboxEl;
2694             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2695             if(opt.prompt){
2696                 if(opt.multiline){
2697                     textboxEl.hide();
2698                     textareaEl.show();
2699                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2700                         opt.multiline : this.defaultTextHeight);
2701                     activeTextEl = textareaEl;
2702                 }else{
2703                     textboxEl.show();
2704                     textareaEl.hide();
2705                 }
2706             }else{
2707                 textboxEl.hide();
2708                 textareaEl.hide();
2709             }
2710             progressEl.setDisplayed(opt.progress === true);
2711             this.updateProgress(0);
2712             activeTextEl.dom.value = opt.value || "";
2713             if(opt.prompt){
2714                 dlg.setDefaultButton(activeTextEl);
2715             }else{
2716                 var bs = opt.buttons;
2717                 var db = null;
2718                 if(bs && bs.ok){
2719                     db = buttons["ok"];
2720                 }else if(bs && bs.yes){
2721                     db = buttons["yes"];
2722                 }
2723                 dlg.setDefaultButton(db);
2724             }
2725             bwidth = updateButtons(opt.buttons);
2726             this.updateText(opt.msg);
2727             if(opt.cls){
2728                 d.el.addClass(opt.cls);
2729             }
2730             d.proxyDrag = opt.proxyDrag === true;
2731             d.modal = opt.modal !== false;
2732             d.mask = opt.modal !== false ? mask : false;
2733             if(!d.isVisible()){
2734                 // force it to the end of the z-index stack so it gets a cursor in FF
2735                 document.body.appendChild(dlg.el.dom);
2736                 d.animateTarget = null;
2737                 d.show(options.animEl);
2738             }
2739             return this;
2740         },
2741
2742         /**
2743          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2744          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2745          * and closing the message box when the process is complete.
2746          * @param {String} title The title bar text
2747          * @param {String} msg The message box body text
2748          * @return {Roo.MessageBox} This message box
2749          */
2750         progress : function(title, msg){
2751             this.show({
2752                 title : title,
2753                 msg : msg,
2754                 buttons: false,
2755                 progress:true,
2756                 closable:false,
2757                 minWidth: this.minProgressWidth,
2758                 modal : true
2759             });
2760             return this;
2761         },
2762
2763         /**
2764          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2765          * If a callback function is passed it will be called after the user clicks the button, and the
2766          * id of the button that was clicked will be passed as the only parameter to the callback
2767          * (could also be the top-right close button).
2768          * @param {String} title The title bar text
2769          * @param {String} msg The message box body text
2770          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2771          * @param {Object} scope (optional) The scope of the callback function
2772          * @return {Roo.MessageBox} This message box
2773          */
2774         alert : function(title, msg, fn, scope){
2775             this.show({
2776                 title : title,
2777                 msg : msg,
2778                 buttons: this.OK,
2779                 fn: fn,
2780                 scope : scope,
2781                 modal : true
2782             });
2783             return this;
2784         },
2785
2786         /**
2787          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2788          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2789          * You are responsible for closing the message box when the process is complete.
2790          * @param {String} msg The message box body text
2791          * @param {String} title (optional) The title bar text
2792          * @return {Roo.MessageBox} This message box
2793          */
2794         wait : function(msg, title){
2795             this.show({
2796                 title : title,
2797                 msg : msg,
2798                 buttons: false,
2799                 closable:false,
2800                 progress:true,
2801                 modal:true,
2802                 width:300,
2803                 wait:true
2804             });
2805             waitTimer = Roo.TaskMgr.start({
2806                 run: function(i){
2807                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2808                 },
2809                 interval: 1000
2810             });
2811             return this;
2812         },
2813
2814         /**
2815          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2816          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2817          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2818          * @param {String} title The title bar text
2819          * @param {String} msg The message box body text
2820          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2821          * @param {Object} scope (optional) The scope of the callback function
2822          * @return {Roo.MessageBox} This message box
2823          */
2824         confirm : function(title, msg, fn, scope){
2825             this.show({
2826                 title : title,
2827                 msg : msg,
2828                 buttons: this.YESNO,
2829                 fn: fn,
2830                 scope : scope,
2831                 modal : true
2832             });
2833             return this;
2834         },
2835
2836         /**
2837          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2838          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2839          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2840          * (could also be the top-right close button) and the text that was entered will be passed as the two
2841          * parameters to the callback.
2842          * @param {String} title The title bar text
2843          * @param {String} msg The message box body text
2844          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2845          * @param {Object} scope (optional) The scope of the callback function
2846          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2847          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2848          * @return {Roo.MessageBox} This message box
2849          */
2850         prompt : function(title, msg, fn, scope, multiline){
2851             this.show({
2852                 title : title,
2853                 msg : msg,
2854                 buttons: this.OKCANCEL,
2855                 fn: fn,
2856                 minWidth:250,
2857                 scope : scope,
2858                 prompt:true,
2859                 multiline: multiline,
2860                 modal : true
2861             });
2862             return this;
2863         },
2864
2865         /**
2866          * Button config that displays a single OK button
2867          * @type Object
2868          */
2869         OK : {ok:true},
2870         /**
2871          * Button config that displays Yes and No buttons
2872          * @type Object
2873          */
2874         YESNO : {yes:true, no:true},
2875         /**
2876          * Button config that displays OK and Cancel buttons
2877          * @type Object
2878          */
2879         OKCANCEL : {ok:true, cancel:true},
2880         /**
2881          * Button config that displays Yes, No and Cancel buttons
2882          * @type Object
2883          */
2884         YESNOCANCEL : {yes:true, no:true, cancel:true},
2885
2886         /**
2887          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2888          * @type Number
2889          */
2890         defaultTextHeight : 75,
2891         /**
2892          * The maximum width in pixels of the message box (defaults to 600)
2893          * @type Number
2894          */
2895         maxWidth : 600,
2896         /**
2897          * The minimum width in pixels of the message box (defaults to 100)
2898          * @type Number
2899          */
2900         minWidth : 100,
2901         /**
2902          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2903          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2904          * @type Number
2905          */
2906         minProgressWidth : 250,
2907         /**
2908          * An object containing the default button text strings that can be overriden for localized language support.
2909          * Supported properties are: ok, cancel, yes and no.
2910          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2911          * @type Object
2912          */
2913         buttonText : {
2914             ok : "OK",
2915             cancel : "Cancel",
2916             yes : "Yes",
2917             no : "No"
2918         }
2919     };
2920 }();
2921
2922 /**
2923  * Shorthand for {@link Roo.MessageBox}
2924  */
2925 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2926 Roo.Msg = Roo.Msg || Roo.MessageBox;
2927 /*
2928  * - LGPL
2929  *
2930  * navbar
2931  * 
2932  */
2933
2934 /**
2935  * @class Roo.bootstrap.Navbar
2936  * @extends Roo.bootstrap.Component
2937  * Bootstrap Navbar class
2938
2939  * @constructor
2940  * Create a new Navbar
2941  * @param {Object} config The config object
2942  */
2943
2944
2945 Roo.bootstrap.Navbar = function(config){
2946     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2947     
2948 };
2949
2950 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2951     
2952     
2953    
2954     // private
2955     navItems : false,
2956     loadMask : false,
2957     
2958     
2959     getAutoCreate : function(){
2960         
2961         
2962         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2963         
2964     },
2965     
2966     initEvents :function ()
2967     {
2968         //Roo.log(this.el.select('.navbar-toggle',true));
2969         this.el.select('.navbar-toggle',true).on('click', function() {
2970            // Roo.log('click');
2971             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2972         }, this);
2973         
2974         var mark = {
2975             tag: "div",
2976             cls:"x-dlg-mask"
2977         }
2978         
2979         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2980         
2981         var size = this.el.getSize();
2982         this.maskEl.setSize(size.width, size.height);
2983         this.maskEl.enableDisplayMode("block");
2984         this.maskEl.hide();
2985         
2986         if(this.loadMask){
2987             this.maskEl.show();
2988         }
2989     },
2990     
2991     
2992     getChildContainer : function()
2993     {
2994         if (this.el.select('.collapse').getCount()) {
2995             return this.el.select('.collapse',true).first();
2996         }
2997         
2998         return this.el;
2999     },
3000     
3001     mask : function()
3002     {
3003         this.maskEl.show();
3004     },
3005     
3006     unmask : function()
3007     {
3008         this.maskEl.hide();
3009     } 
3010     
3011     
3012     
3013     
3014 });
3015
3016
3017
3018  
3019
3020  /*
3021  * - LGPL
3022  *
3023  * navbar
3024  * 
3025  */
3026
3027 /**
3028  * @class Roo.bootstrap.NavSimplebar
3029  * @extends Roo.bootstrap.Navbar
3030  * Bootstrap Sidebar class
3031  *
3032  * @cfg {Boolean} inverse is inverted color
3033  * 
3034  * @cfg {String} type (nav | pills | tabs)
3035  * @cfg {Boolean} arrangement stacked | justified
3036  * @cfg {String} align (left | right) alignment
3037  * 
3038  * @cfg {Boolean} main (true|false) main nav bar? default false
3039  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3040  * 
3041  * @cfg {String} tag (header|footer|nav|div) default is nav 
3042
3043  * 
3044  * 
3045  * 
3046  * @constructor
3047  * Create a new Sidebar
3048  * @param {Object} config The config object
3049  */
3050
3051
3052 Roo.bootstrap.NavSimplebar = function(config){
3053     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3054 };
3055
3056 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3057     
3058     inverse: false,
3059     
3060     type: false,
3061     arrangement: '',
3062     align : false,
3063     
3064     
3065     
3066     main : false,
3067     
3068     
3069     tag : false,
3070     
3071     
3072     getAutoCreate : function(){
3073         
3074         
3075         var cfg = {
3076             tag : this.tag || 'div',
3077             cls : 'navbar'
3078         };
3079           
3080         
3081         cfg.cn = [
3082             {
3083                 cls: 'nav',
3084                 tag : 'ul'
3085             }
3086         ];
3087         
3088          
3089         this.type = this.type || 'nav';
3090         if (['tabs','pills'].indexOf(this.type)!==-1) {
3091             cfg.cn[0].cls += ' nav-' + this.type
3092         
3093         
3094         } else {
3095             if (this.type!=='nav') {
3096                 Roo.log('nav type must be nav/tabs/pills')
3097             }
3098             cfg.cn[0].cls += ' navbar-nav'
3099         }
3100         
3101         
3102         
3103         
3104         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3105             cfg.cn[0].cls += ' nav-' + this.arrangement;
3106         }
3107         
3108         
3109         if (this.align === 'right') {
3110             cfg.cn[0].cls += ' navbar-right';
3111         }
3112         
3113         if (this.inverse) {
3114             cfg.cls += ' navbar-inverse';
3115             
3116         }
3117         
3118         
3119         return cfg;
3120     
3121         
3122     }
3123     
3124     
3125     
3126 });
3127
3128
3129
3130  
3131
3132  
3133        /*
3134  * - LGPL
3135  *
3136  * navbar
3137  * 
3138  */
3139
3140 /**
3141  * @class Roo.bootstrap.NavHeaderbar
3142  * @extends Roo.bootstrap.NavSimplebar
3143  * Bootstrap Sidebar class
3144  *
3145  * @cfg {String} brand what is brand
3146  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3147  * @cfg {String} brand_href href of the brand
3148  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3149  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3150  * 
3151  * @constructor
3152  * Create a new Sidebar
3153  * @param {Object} config The config object
3154  */
3155
3156
3157 Roo.bootstrap.NavHeaderbar = function(config){
3158     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3159 };
3160
3161 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3162     
3163     position: '',
3164     brand: '',
3165     brand_href: false,
3166     srButton : true,
3167     autohide : false,
3168     
3169     getAutoCreate : function(){
3170         
3171         var   cfg = {
3172             tag: this.nav || 'nav',
3173             cls: 'navbar',
3174             role: 'navigation',
3175             cn: []
3176         };
3177         
3178         if(this.srButton){
3179             cfg.cn.push({
3180                 tag: 'div',
3181                 cls: 'navbar-header',
3182                 cn: [
3183                     {
3184                         tag: 'button',
3185                         type: 'button',
3186                         cls: 'navbar-toggle',
3187                         'data-toggle': 'collapse',
3188                         cn: [
3189                             {
3190                                 tag: 'span',
3191                                 cls: 'sr-only',
3192                                 html: 'Toggle navigation'
3193                             },
3194                             {
3195                                 tag: 'span',
3196                                 cls: 'icon-bar'
3197                             },
3198                             {
3199                                 tag: 'span',
3200                                 cls: 'icon-bar'
3201                             },
3202                             {
3203                                 tag: 'span',
3204                                 cls: 'icon-bar'
3205                             }
3206                         ]
3207                     }
3208                 ]
3209             });
3210         }
3211         
3212         cfg.cn.push({
3213             tag: 'div',
3214             cls: 'collapse navbar-collapse',
3215             cn : []
3216         });
3217         
3218         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3219         
3220         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3221             cfg.cls += ' navbar-' + this.position;
3222             
3223             // tag can override this..
3224             
3225             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3226         }
3227         
3228         if (this.brand !== '') {
3229             cfg.cn[0].cn.push({
3230                 tag: 'a',
3231                 href: this.brand_href ? this.brand_href : '#',
3232                 cls: 'navbar-brand',
3233                 cn: [
3234                 this.brand
3235                 ]
3236             });
3237         }
3238         
3239         if(this.main){
3240             cfg.cls += ' main-nav';
3241         }
3242         
3243         
3244         return cfg;
3245
3246         
3247     },
3248     initEvents : function()
3249     {
3250         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3251         
3252         if (this.autohide) {
3253             
3254             var prevScroll = 0;
3255             var ft = this.el;
3256             
3257             Roo.get(document).on('scroll',function(e) {
3258                 var ns = Roo.get(document).getScroll().top;
3259                 var os = prevScroll;
3260                 prevScroll = ns;
3261                 
3262                 if(ns > os){
3263                     ft.removeClass('slideDown');
3264                     ft.addClass('slideUp');
3265                     return;
3266                 }
3267                 ft.removeClass('slideUp');
3268                 ft.addClass('slideDown');
3269                  
3270               
3271           },this);
3272         }
3273     }    
3274           
3275       
3276     
3277     
3278 });
3279
3280
3281
3282  
3283
3284  /*
3285  * - LGPL
3286  *
3287  * navbar
3288  * 
3289  */
3290
3291 /**
3292  * @class Roo.bootstrap.NavSidebar
3293  * @extends Roo.bootstrap.Navbar
3294  * Bootstrap Sidebar class
3295  * 
3296  * @constructor
3297  * Create a new Sidebar
3298  * @param {Object} config The config object
3299  */
3300
3301
3302 Roo.bootstrap.NavSidebar = function(config){
3303     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3304 };
3305
3306 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3307     
3308     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3309     
3310     getAutoCreate : function(){
3311         
3312         
3313         return  {
3314             tag: 'div',
3315             cls: 'sidebar sidebar-nav'
3316         };
3317     
3318         
3319     }
3320     
3321     
3322     
3323 });
3324
3325
3326
3327  
3328
3329  /*
3330  * - LGPL
3331  *
3332  * nav group
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.NavGroup
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap NavGroup class
3340  * @cfg {String} align left | right
3341  * @cfg {Boolean} inverse false | true
3342  * @cfg {String} type (nav|pills|tab) default nav
3343  * @cfg {String} navId - reference Id for navbar.
3344
3345  * 
3346  * @constructor
3347  * Create a new nav group
3348  * @param {Object} config The config object
3349  */
3350
3351 Roo.bootstrap.NavGroup = function(config){
3352     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3353     this.navItems = [];
3354    
3355     Roo.bootstrap.NavGroup.register(this);
3356      this.addEvents({
3357         /**
3358              * @event changed
3359              * Fires when the active item changes
3360              * @param {Roo.bootstrap.NavGroup} this
3361              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3362              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3363          */
3364         'changed': true
3365      });
3366     
3367 };
3368
3369 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3370     
3371     align: '',
3372     inverse: false,
3373     form: false,
3374     type: 'nav',
3375     navId : '',
3376     // private
3377     
3378     navItems : false, 
3379     
3380     getAutoCreate : function()
3381     {
3382         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3383         
3384         cfg = {
3385             tag : 'ul',
3386             cls: 'nav' 
3387         }
3388         
3389         if (['tabs','pills'].indexOf(this.type)!==-1) {
3390             cfg.cls += ' nav-' + this.type
3391         } else {
3392             if (this.type!=='nav') {
3393                 Roo.log('nav type must be nav/tabs/pills')
3394             }
3395             cfg.cls += ' navbar-nav'
3396         }
3397         
3398         if (this.parent().sidebar) {
3399             cfg = {
3400                 tag: 'ul',
3401                 cls: 'dashboard-menu sidebar-menu'
3402             }
3403             
3404             return cfg;
3405         }
3406         
3407         if (this.form === true) {
3408             cfg = {
3409                 tag: 'form',
3410                 cls: 'navbar-form'
3411             }
3412             
3413             if (this.align === 'right') {
3414                 cfg.cls += ' navbar-right';
3415             } else {
3416                 cfg.cls += ' navbar-left';
3417             }
3418         }
3419         
3420         if (this.align === 'right') {
3421             cfg.cls += ' navbar-right';
3422         }
3423         
3424         if (this.inverse) {
3425             cfg.cls += ' navbar-inverse';
3426             
3427         }
3428         
3429         
3430         return cfg;
3431     },
3432     /**
3433     * sets the active Navigation item
3434     * @param {Roo.bootstrap.NavItem} the new current navitem
3435     */
3436     setActiveItem : function(item)
3437     {
3438         var prev = false;
3439         Roo.each(this.navItems, function(v){
3440             if (v == item) {
3441                 return ;
3442             }
3443             if (v.isActive()) {
3444                 v.setActive(false, true);
3445                 prev = v;
3446                 
3447             }
3448             
3449         });
3450
3451         item.setActive(true, true);
3452         this.fireEvent('changed', this, item, prev);
3453         
3454         
3455     },
3456     /**
3457     * gets the active Navigation item
3458     * @return {Roo.bootstrap.NavItem} the current navitem
3459     */
3460     getActive : function()
3461     {
3462         
3463         var prev = false;
3464         Roo.each(this.navItems, function(v){
3465             
3466             if (v.isActive()) {
3467                 prev = v;
3468                 
3469             }
3470             
3471         });
3472         return prev;
3473     },
3474     
3475     indexOfNav : function()
3476     {
3477         
3478         var prev = false;
3479         Roo.each(this.navItems, function(v,i){
3480             
3481             if (v.isActive()) {
3482                 prev = i;
3483                 
3484             }
3485             
3486         });
3487         return prev;
3488     },
3489     /**
3490     * adds a Navigation item
3491     * @param {Roo.bootstrap.NavItem} the navitem to add
3492     */
3493     addItem : function(cfg)
3494     {
3495         var cn = new Roo.bootstrap.NavItem(cfg);
3496         this.register(cn);
3497         cn.parentId = this.id;
3498         cn.onRender(this.el, null);
3499         return cn;
3500     },
3501     /**
3502     * register a Navigation item
3503     * @param {Roo.bootstrap.NavItem} the navitem to add
3504     */
3505     register : function(item)
3506     {
3507         this.navItems.push( item);
3508         item.navId = this.navId;
3509     
3510     },
3511     
3512     /**
3513     * clear all the Navigation item
3514     */
3515    
3516     clearAll : function()
3517     {
3518         this.navItems = [];
3519         this.el.dom.innerHTML = '';
3520     },
3521     
3522     getNavItem: function(tabId)
3523     {
3524         var ret = false;
3525         Roo.each(this.navItems, function(e) {
3526             if (e.tabId == tabId) {
3527                ret =  e;
3528                return false;
3529             }
3530             return true;
3531             
3532         });
3533         return ret;
3534     },
3535     
3536     setActiveNext : function()
3537     {
3538         var i = this.indexOfNav(this.getActive());
3539         if (i > this.navItems.length) {
3540             return;
3541         }
3542         this.setActiveItem(this.navItems[i+1]);
3543     },
3544     setActivePrev : function()
3545     {
3546         var i = this.indexOfNav(this.getActive());
3547         if (i  < 1) {
3548             return;
3549         }
3550         this.setActiveItem(this.navItems[i-1]);
3551     },
3552     clearWasActive : function(except) {
3553         Roo.each(this.navItems, function(e) {
3554             if (e.tabId != except.tabId && e.was_active) {
3555                e.was_active = false;
3556                return false;
3557             }
3558             return true;
3559             
3560         });
3561     },
3562     getWasActive : function ()
3563     {
3564         var r = false;
3565         Roo.each(this.navItems, function(e) {
3566             if (e.was_active) {
3567                r = e;
3568                return false;
3569             }
3570             return true;
3571             
3572         });
3573         return r;
3574     }
3575     
3576     
3577 });
3578
3579  
3580 Roo.apply(Roo.bootstrap.NavGroup, {
3581     
3582     groups: {},
3583      /**
3584     * register a Navigation Group
3585     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3586     */
3587     register : function(navgrp)
3588     {
3589         this.groups[navgrp.navId] = navgrp;
3590         
3591     },
3592     /**
3593     * fetch a Navigation Group based on the navigation ID
3594     * @param {string} the navgroup to add
3595     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3596     */
3597     get: function(navId) {
3598         if (typeof(this.groups[navId]) == 'undefined') {
3599             return false;
3600             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3601         }
3602         return this.groups[navId] ;
3603     }
3604     
3605     
3606     
3607 });
3608
3609  /*
3610  * - LGPL
3611  *
3612  * row
3613  * 
3614  */
3615
3616 /**
3617  * @class Roo.bootstrap.NavItem
3618  * @extends Roo.bootstrap.Component
3619  * Bootstrap Navbar.NavItem class
3620  * @cfg {String} href  link to
3621  * @cfg {String} html content of button
3622  * @cfg {String} badge text inside badge
3623  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3624  * @cfg {String} glyphicon name of glyphicon
3625  * @cfg {String} icon name of font awesome icon
3626  * @cfg {Boolean} active Is item active
3627  * @cfg {Boolean} disabled Is item disabled
3628  
3629  * @cfg {Boolean} preventDefault (true | false) default false
3630  * @cfg {String} tabId the tab that this item activates.
3631  * @cfg {String} tagtype (a|span) render as a href or span?
3632   
3633  * @constructor
3634  * Create a new Navbar Item
3635  * @param {Object} config The config object
3636  */
3637 Roo.bootstrap.NavItem = function(config){
3638     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3639     this.addEvents({
3640         // raw events
3641         /**
3642          * @event click
3643          * The raw click event for the entire grid.
3644          * @param {Roo.EventObject} e
3645          */
3646         "click" : true,
3647          /**
3648             * @event changed
3649             * Fires when the active item active state changes
3650             * @param {Roo.bootstrap.NavItem} this
3651             * @param {boolean} state the new state
3652              
3653          */
3654         'changed': true
3655     });
3656    
3657 };
3658
3659 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3660     
3661     href: false,
3662     html: '',
3663     badge: '',
3664     icon: false,
3665     glyphicon: false,
3666     active: false,
3667     preventDefault : false,
3668     tabId : false,
3669     tagtype : 'a',
3670     disabled : false,
3671     
3672     was_active : false,
3673     
3674     getAutoCreate : function(){
3675          
3676         var cfg = {
3677             tag: 'li',
3678             cls: 'nav-item'
3679             
3680         }
3681         if (this.active) {
3682             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3683         }
3684         if (this.disabled) {
3685             cfg.cls += ' disabled';
3686         }
3687         
3688         if (this.href || this.html || this.glyphicon || this.icon) {
3689             cfg.cn = [
3690                 {
3691                     tag: this.tagtype,
3692                     href : this.href || "#",
3693                     html: this.html || ''
3694                 }
3695             ];
3696             
3697             if (this.icon) {
3698                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3699             }
3700
3701             if(this.glyphicon) {
3702                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3703             }
3704             
3705             if (this.menu) {
3706                 
3707                 cfg.cn[0].html += " <span class='caret'></span>";
3708              
3709             }
3710             
3711             if (this.badge !== '') {
3712                  
3713                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3714             }
3715         }
3716         
3717         
3718         
3719         return cfg;
3720     },
3721     initEvents: function() {
3722        // Roo.log('init events?');
3723        // Roo.log(this.el.dom);
3724         if (typeof (this.menu) != 'undefined') {
3725             this.menu.parentType = this.xtype;
3726             this.menu.triggerEl = this.el;
3727             this.addxtype(Roo.apply({}, this.menu));
3728         }
3729
3730        
3731         this.el.select('a',true).on('click', this.onClick, this);
3732         // at this point parent should be available..
3733         this.parent().register(this);
3734     },
3735     
3736     onClick : function(e)
3737     {
3738          
3739         if(this.preventDefault){
3740             e.preventDefault();
3741         }
3742         if (this.disabled) {
3743             return;
3744         }
3745         
3746         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3747         if (tg && tg.transition) {
3748             Roo.log("waiting for the transitionend");
3749             return;
3750         }
3751         
3752         Roo.log("fire event clicked");
3753         if(this.fireEvent('click', this, e) === false){
3754             return;
3755         };
3756         var p = this.parent();
3757         if (['tabs','pills'].indexOf(p.type)!==-1) {
3758             if (typeof(p.setActiveItem) !== 'undefined') {
3759                 p.setActiveItem(this);
3760             }
3761         }
3762         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3763         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3764             // remove the collapsed menu expand...
3765             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3766         }
3767         
3768     },
3769     
3770     isActive: function () {
3771         return this.active
3772     },
3773     setActive : function(state, fire, is_was_active)
3774     {
3775         if (this.active && !state & this.navId) {
3776             this.was_active = true;
3777             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3778             if (nv) {
3779                 nv.clearWasActive(this);
3780             }
3781             
3782         }
3783         this.active = state;
3784         
3785         if (!state ) {
3786             this.el.removeClass('active');
3787         } else if (!this.el.hasClass('active')) {
3788             this.el.addClass('active');
3789         }
3790         if (fire) {
3791             this.fireEvent('changed', this, state);
3792         }
3793         
3794         // show a panel if it's registered and related..
3795         
3796         if (!this.navId || !this.tabId || !state || is_was_active) {
3797             return;
3798         }
3799         
3800         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3801         if (!tg) {
3802             return;
3803         }
3804         var pan = tg.getPanelByName(this.tabId);
3805         if (!pan) {
3806             return;
3807         }
3808         // if we can not flip to new panel - go back to old nav highlight..
3809         if (false == tg.showPanel(pan)) {
3810             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3811             if (nv) {
3812                 var onav = nv.getWasActive();
3813                 if (onav) {
3814                     onav.setActive(true, false, true);
3815                 }
3816             }
3817             
3818         }
3819         
3820         
3821         
3822     },
3823      // this should not be here...
3824     setDisabled : function(state)
3825     {
3826         this.disabled = state;
3827         if (!state ) {
3828             this.el.removeClass('disabled');
3829         } else if (!this.el.hasClass('disabled')) {
3830             this.el.addClass('disabled');
3831         }
3832         
3833     }
3834 });
3835  
3836
3837  /*
3838  * - LGPL
3839  *
3840  * sidebar item
3841  *
3842  *  li
3843  *    <span> icon </span>
3844  *    <span> text </span>
3845  *    <span>badge </span>
3846  */
3847
3848 /**
3849  * @class Roo.bootstrap.NavSidebarItem
3850  * @extends Roo.bootstrap.NavItem
3851  * Bootstrap Navbar.NavSidebarItem class
3852  * @constructor
3853  * Create a new Navbar Button
3854  * @param {Object} config The config object
3855  */
3856 Roo.bootstrap.NavSidebarItem = function(config){
3857     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3858     this.addEvents({
3859         // raw events
3860         /**
3861          * @event click
3862          * The raw click event for the entire grid.
3863          * @param {Roo.EventObject} e
3864          */
3865         "click" : true,
3866          /**
3867             * @event changed
3868             * Fires when the active item active state changes
3869             * @param {Roo.bootstrap.NavSidebarItem} this
3870             * @param {boolean} state the new state
3871              
3872          */
3873         'changed': true
3874     });
3875    
3876 };
3877
3878 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3879     
3880     
3881     getAutoCreate : function(){
3882         
3883         
3884         var a = {
3885                 tag: 'a',
3886                 href : this.href || '#',
3887                 cls: '',
3888                 html : '',
3889                 cn : []
3890         };
3891         var cfg = {
3892             tag: 'li',
3893             cls: '',
3894             cn: [ a ]
3895         }
3896         var span = {
3897             tag: 'span',
3898             html : this.html || ''
3899         }
3900         
3901         
3902         if (this.active) {
3903             cfg.cls += ' active';
3904         }
3905         
3906         // left icon..
3907         if (this.glyphicon || this.icon) {
3908             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3909             a.cn.push({ tag : 'i', cls : c }) ;
3910         }
3911         // html..
3912         a.cn.push(span);
3913         // then badge..
3914         if (this.badge !== '') {
3915             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3916         }
3917         // fi
3918         if (this.menu) {
3919             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3920             a.cls += 'dropdown-toggle treeview' ;
3921             
3922         }
3923         
3924         
3925         
3926         return cfg;
3927          
3928            
3929     }
3930    
3931      
3932  
3933 });
3934  
3935
3936  /*
3937  * - LGPL
3938  *
3939  * row
3940  * 
3941  */
3942
3943 /**
3944  * @class Roo.bootstrap.Row
3945  * @extends Roo.bootstrap.Component
3946  * Bootstrap Row class (contains columns...)
3947  * 
3948  * @constructor
3949  * Create a new Row
3950  * @param {Object} config The config object
3951  */
3952
3953 Roo.bootstrap.Row = function(config){
3954     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3955 };
3956
3957 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3958     
3959     getAutoCreate : function(){
3960        return {
3961             cls: 'row clearfix'
3962        };
3963     }
3964     
3965     
3966 });
3967
3968  
3969
3970  /*
3971  * - LGPL
3972  *
3973  * element
3974  * 
3975  */
3976
3977 /**
3978  * @class Roo.bootstrap.Element
3979  * @extends Roo.bootstrap.Component
3980  * Bootstrap Element class
3981  * @cfg {String} html contents of the element
3982  * @cfg {String} tag tag of the element
3983  * @cfg {String} cls class of the element
3984  * 
3985  * @constructor
3986  * Create a new Element
3987  * @param {Object} config The config object
3988  */
3989
3990 Roo.bootstrap.Element = function(config){
3991     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3992 };
3993
3994 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3995     
3996     tag: 'div',
3997     cls: '',
3998     html: '',
3999      
4000     
4001     getAutoCreate : function(){
4002         
4003         var cfg = {
4004             tag: this.tag,
4005             cls: this.cls,
4006             html: this.html
4007         }
4008         
4009         
4010         
4011         return cfg;
4012     }
4013    
4014 });
4015
4016  
4017
4018  /*
4019  * - LGPL
4020  *
4021  * pagination
4022  * 
4023  */
4024
4025 /**
4026  * @class Roo.bootstrap.Pagination
4027  * @extends Roo.bootstrap.Component
4028  * Bootstrap Pagination class
4029  * @cfg {String} size xs | sm | md | lg
4030  * @cfg {Boolean} inverse false | true
4031  * 
4032  * @constructor
4033  * Create a new Pagination
4034  * @param {Object} config The config object
4035  */
4036
4037 Roo.bootstrap.Pagination = function(config){
4038     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4039 };
4040
4041 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4042     
4043     cls: false,
4044     size: false,
4045     inverse: false,
4046     
4047     getAutoCreate : function(){
4048         var cfg = {
4049             tag: 'ul',
4050                 cls: 'pagination'
4051         };
4052         if (this.inverse) {
4053             cfg.cls += ' inverse';
4054         }
4055         if (this.html) {
4056             cfg.html=this.html;
4057         }
4058         if (this.cls) {
4059             cfg.cls += " " + this.cls;
4060         }
4061         return cfg;
4062     }
4063    
4064 });
4065
4066  
4067
4068  /*
4069  * - LGPL
4070  *
4071  * Pagination item
4072  * 
4073  */
4074
4075
4076 /**
4077  * @class Roo.bootstrap.PaginationItem
4078  * @extends Roo.bootstrap.Component
4079  * Bootstrap PaginationItem class
4080  * @cfg {String} html text
4081  * @cfg {String} href the link
4082  * @cfg {Boolean} preventDefault (true | false) default true
4083  * @cfg {Boolean} active (true | false) default false
4084  * 
4085  * 
4086  * @constructor
4087  * Create a new PaginationItem
4088  * @param {Object} config The config object
4089  */
4090
4091
4092 Roo.bootstrap.PaginationItem = function(config){
4093     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4094     this.addEvents({
4095         // raw events
4096         /**
4097          * @event click
4098          * The raw click event for the entire grid.
4099          * @param {Roo.EventObject} e
4100          */
4101         "click" : true
4102     });
4103 };
4104
4105 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4106     
4107     href : false,
4108     html : false,
4109     preventDefault: true,
4110     active : false,
4111     cls : false,
4112     
4113     getAutoCreate : function(){
4114         var cfg= {
4115             tag: 'li',
4116             cn: [
4117                 {
4118                     tag : 'a',
4119                     href : this.href ? this.href : '#',
4120                     html : this.html ? this.html : ''
4121                 }
4122             ]
4123         };
4124         
4125         if(this.cls){
4126             cfg.cls = this.cls;
4127         }
4128         
4129         if(this.active){
4130             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4131         }
4132         
4133         return cfg;
4134     },
4135     
4136     initEvents: function() {
4137         
4138         this.el.on('click', this.onClick, this);
4139         
4140     },
4141     onClick : function(e)
4142     {
4143         Roo.log('PaginationItem on click ');
4144         if(this.preventDefault){
4145             e.preventDefault();
4146         }
4147         
4148         this.fireEvent('click', this, e);
4149     }
4150    
4151 });
4152
4153  
4154
4155  /*
4156  * - LGPL
4157  *
4158  * slider
4159  * 
4160  */
4161
4162
4163 /**
4164  * @class Roo.bootstrap.Slider
4165  * @extends Roo.bootstrap.Component
4166  * Bootstrap Slider class
4167  *    
4168  * @constructor
4169  * Create a new Slider
4170  * @param {Object} config The config object
4171  */
4172
4173 Roo.bootstrap.Slider = function(config){
4174     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4175 };
4176
4177 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4178     
4179     getAutoCreate : function(){
4180         
4181         var cfg = {
4182             tag: 'div',
4183             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4184             cn: [
4185                 {
4186                     tag: 'a',
4187                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4188                 }
4189             ]
4190         }
4191         
4192         return cfg;
4193     }
4194    
4195 });
4196
4197  /*
4198  * Based on:
4199  * Ext JS Library 1.1.1
4200  * Copyright(c) 2006-2007, Ext JS, LLC.
4201  *
4202  * Originally Released Under LGPL - original licence link has changed is not relivant.
4203  *
4204  * Fork - LGPL
4205  * <script type="text/javascript">
4206  */
4207  
4208
4209 /**
4210  * @class Roo.grid.ColumnModel
4211  * @extends Roo.util.Observable
4212  * This is the default implementation of a ColumnModel used by the Grid. It defines
4213  * the columns in the grid.
4214  * <br>Usage:<br>
4215  <pre><code>
4216  var colModel = new Roo.grid.ColumnModel([
4217         {header: "Ticker", width: 60, sortable: true, locked: true},
4218         {header: "Company Name", width: 150, sortable: true},
4219         {header: "Market Cap.", width: 100, sortable: true},
4220         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4221         {header: "Employees", width: 100, sortable: true, resizable: false}
4222  ]);
4223  </code></pre>
4224  * <p>
4225  
4226  * The config options listed for this class are options which may appear in each
4227  * individual column definition.
4228  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4229  * @constructor
4230  * @param {Object} config An Array of column config objects. See this class's
4231  * config objects for details.
4232 */
4233 Roo.grid.ColumnModel = function(config){
4234         /**
4235      * The config passed into the constructor
4236      */
4237     this.config = config;
4238     this.lookup = {};
4239
4240     // if no id, create one
4241     // if the column does not have a dataIndex mapping,
4242     // map it to the order it is in the config
4243     for(var i = 0, len = config.length; i < len; i++){
4244         var c = config[i];
4245         if(typeof c.dataIndex == "undefined"){
4246             c.dataIndex = i;
4247         }
4248         if(typeof c.renderer == "string"){
4249             c.renderer = Roo.util.Format[c.renderer];
4250         }
4251         if(typeof c.id == "undefined"){
4252             c.id = Roo.id();
4253         }
4254         if(c.editor && c.editor.xtype){
4255             c.editor  = Roo.factory(c.editor, Roo.grid);
4256         }
4257         if(c.editor && c.editor.isFormField){
4258             c.editor = new Roo.grid.GridEditor(c.editor);
4259         }
4260         this.lookup[c.id] = c;
4261     }
4262
4263     /**
4264      * The width of columns which have no width specified (defaults to 100)
4265      * @type Number
4266      */
4267     this.defaultWidth = 100;
4268
4269     /**
4270      * Default sortable of columns which have no sortable specified (defaults to false)
4271      * @type Boolean
4272      */
4273     this.defaultSortable = false;
4274
4275     this.addEvents({
4276         /**
4277              * @event widthchange
4278              * Fires when the width of a column changes.
4279              * @param {ColumnModel} this
4280              * @param {Number} columnIndex The column index
4281              * @param {Number} newWidth The new width
4282              */
4283             "widthchange": true,
4284         /**
4285              * @event headerchange
4286              * Fires when the text of a header changes.
4287              * @param {ColumnModel} this
4288              * @param {Number} columnIndex The column index
4289              * @param {Number} newText The new header text
4290              */
4291             "headerchange": true,
4292         /**
4293              * @event hiddenchange
4294              * Fires when a column is hidden or "unhidden".
4295              * @param {ColumnModel} this
4296              * @param {Number} columnIndex The column index
4297              * @param {Boolean} hidden true if hidden, false otherwise
4298              */
4299             "hiddenchange": true,
4300             /**
4301          * @event columnmoved
4302          * Fires when a column is moved.
4303          * @param {ColumnModel} this
4304          * @param {Number} oldIndex
4305          * @param {Number} newIndex
4306          */
4307         "columnmoved" : true,
4308         /**
4309          * @event columlockchange
4310          * Fires when a column's locked state is changed
4311          * @param {ColumnModel} this
4312          * @param {Number} colIndex
4313          * @param {Boolean} locked true if locked
4314          */
4315         "columnlockchange" : true
4316     });
4317     Roo.grid.ColumnModel.superclass.constructor.call(this);
4318 };
4319 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4320     /**
4321      * @cfg {String} header The header text to display in the Grid view.
4322      */
4323     /**
4324      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4325      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4326      * specified, the column's index is used as an index into the Record's data Array.
4327      */
4328     /**
4329      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4330      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4331      */
4332     /**
4333      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4334      * Defaults to the value of the {@link #defaultSortable} property.
4335      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4336      */
4337     /**
4338      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4339      */
4340     /**
4341      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4342      */
4343     /**
4344      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4345      */
4346     /**
4347      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4348      */
4349     /**
4350      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4351      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4352      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4353      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4354      */
4355        /**
4356      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4357      */
4358     /**
4359      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4360      */
4361
4362     /**
4363      * Returns the id of the column at the specified index.
4364      * @param {Number} index The column index
4365      * @return {String} the id
4366      */
4367     getColumnId : function(index){
4368         return this.config[index].id;
4369     },
4370
4371     /**
4372      * Returns the column for a specified id.
4373      * @param {String} id The column id
4374      * @return {Object} the column
4375      */
4376     getColumnById : function(id){
4377         return this.lookup[id];
4378     },
4379
4380     
4381     /**
4382      * Returns the column for a specified dataIndex.
4383      * @param {String} dataIndex The column dataIndex
4384      * @return {Object|Boolean} the column or false if not found
4385      */
4386     getColumnByDataIndex: function(dataIndex){
4387         var index = this.findColumnIndex(dataIndex);
4388         return index > -1 ? this.config[index] : false;
4389     },
4390     
4391     /**
4392      * Returns the index for a specified column id.
4393      * @param {String} id The column id
4394      * @return {Number} the index, or -1 if not found
4395      */
4396     getIndexById : function(id){
4397         for(var i = 0, len = this.config.length; i < len; i++){
4398             if(this.config[i].id == id){
4399                 return i;
4400             }
4401         }
4402         return -1;
4403     },
4404     
4405     /**
4406      * Returns the index for a specified column dataIndex.
4407      * @param {String} dataIndex The column dataIndex
4408      * @return {Number} the index, or -1 if not found
4409      */
4410     
4411     findColumnIndex : function(dataIndex){
4412         for(var i = 0, len = this.config.length; i < len; i++){
4413             if(this.config[i].dataIndex == dataIndex){
4414                 return i;
4415             }
4416         }
4417         return -1;
4418     },
4419     
4420     
4421     moveColumn : function(oldIndex, newIndex){
4422         var c = this.config[oldIndex];
4423         this.config.splice(oldIndex, 1);
4424         this.config.splice(newIndex, 0, c);
4425         this.dataMap = null;
4426         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4427     },
4428
4429     isLocked : function(colIndex){
4430         return this.config[colIndex].locked === true;
4431     },
4432
4433     setLocked : function(colIndex, value, suppressEvent){
4434         if(this.isLocked(colIndex) == value){
4435             return;
4436         }
4437         this.config[colIndex].locked = value;
4438         if(!suppressEvent){
4439             this.fireEvent("columnlockchange", this, colIndex, value);
4440         }
4441     },
4442
4443     getTotalLockedWidth : function(){
4444         var totalWidth = 0;
4445         for(var i = 0; i < this.config.length; i++){
4446             if(this.isLocked(i) && !this.isHidden(i)){
4447                 this.totalWidth += this.getColumnWidth(i);
4448             }
4449         }
4450         return totalWidth;
4451     },
4452
4453     getLockedCount : function(){
4454         for(var i = 0, len = this.config.length; i < len; i++){
4455             if(!this.isLocked(i)){
4456                 return i;
4457             }
4458         }
4459     },
4460
4461     /**
4462      * Returns the number of columns.
4463      * @return {Number}
4464      */
4465     getColumnCount : function(visibleOnly){
4466         if(visibleOnly === true){
4467             var c = 0;
4468             for(var i = 0, len = this.config.length; i < len; i++){
4469                 if(!this.isHidden(i)){
4470                     c++;
4471                 }
4472             }
4473             return c;
4474         }
4475         return this.config.length;
4476     },
4477
4478     /**
4479      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4480      * @param {Function} fn
4481      * @param {Object} scope (optional)
4482      * @return {Array} result
4483      */
4484     getColumnsBy : function(fn, scope){
4485         var r = [];
4486         for(var i = 0, len = this.config.length; i < len; i++){
4487             var c = this.config[i];
4488             if(fn.call(scope||this, c, i) === true){
4489                 r[r.length] = c;
4490             }
4491         }
4492         return r;
4493     },
4494
4495     /**
4496      * Returns true if the specified column is sortable.
4497      * @param {Number} col The column index
4498      * @return {Boolean}
4499      */
4500     isSortable : function(col){
4501         if(typeof this.config[col].sortable == "undefined"){
4502             return this.defaultSortable;
4503         }
4504         return this.config[col].sortable;
4505     },
4506
4507     /**
4508      * Returns the rendering (formatting) function defined for the column.
4509      * @param {Number} col The column index.
4510      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4511      */
4512     getRenderer : function(col){
4513         if(!this.config[col].renderer){
4514             return Roo.grid.ColumnModel.defaultRenderer;
4515         }
4516         return this.config[col].renderer;
4517     },
4518
4519     /**
4520      * Sets the rendering (formatting) function for a column.
4521      * @param {Number} col The column index
4522      * @param {Function} fn The function to use to process the cell's raw data
4523      * to return HTML markup for the grid view. The render function is called with
4524      * the following parameters:<ul>
4525      * <li>Data value.</li>
4526      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4527      * <li>css A CSS style string to apply to the table cell.</li>
4528      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4529      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4530      * <li>Row index</li>
4531      * <li>Column index</li>
4532      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4533      */
4534     setRenderer : function(col, fn){
4535         this.config[col].renderer = fn;
4536     },
4537
4538     /**
4539      * Returns the width for the specified column.
4540      * @param {Number} col The column index
4541      * @return {Number}
4542      */
4543     getColumnWidth : function(col){
4544         return this.config[col].width * 1 || this.defaultWidth;
4545     },
4546
4547     /**
4548      * Sets the width for a column.
4549      * @param {Number} col The column index
4550      * @param {Number} width The new width
4551      */
4552     setColumnWidth : function(col, width, suppressEvent){
4553         this.config[col].width = width;
4554         this.totalWidth = null;
4555         if(!suppressEvent){
4556              this.fireEvent("widthchange", this, col, width);
4557         }
4558     },
4559
4560     /**
4561      * Returns the total width of all columns.
4562      * @param {Boolean} includeHidden True to include hidden column widths
4563      * @return {Number}
4564      */
4565     getTotalWidth : function(includeHidden){
4566         if(!this.totalWidth){
4567             this.totalWidth = 0;
4568             for(var i = 0, len = this.config.length; i < len; i++){
4569                 if(includeHidden || !this.isHidden(i)){
4570                     this.totalWidth += this.getColumnWidth(i);
4571                 }
4572             }
4573         }
4574         return this.totalWidth;
4575     },
4576
4577     /**
4578      * Returns the header for the specified column.
4579      * @param {Number} col The column index
4580      * @return {String}
4581      */
4582     getColumnHeader : function(col){
4583         return this.config[col].header;
4584     },
4585
4586     /**
4587      * Sets the header for a column.
4588      * @param {Number} col The column index
4589      * @param {String} header The new header
4590      */
4591     setColumnHeader : function(col, header){
4592         this.config[col].header = header;
4593         this.fireEvent("headerchange", this, col, header);
4594     },
4595
4596     /**
4597      * Returns the tooltip for the specified column.
4598      * @param {Number} col The column index
4599      * @return {String}
4600      */
4601     getColumnTooltip : function(col){
4602             return this.config[col].tooltip;
4603     },
4604     /**
4605      * Sets the tooltip for a column.
4606      * @param {Number} col The column index
4607      * @param {String} tooltip The new tooltip
4608      */
4609     setColumnTooltip : function(col, tooltip){
4610             this.config[col].tooltip = tooltip;
4611     },
4612
4613     /**
4614      * Returns the dataIndex for the specified column.
4615      * @param {Number} col The column index
4616      * @return {Number}
4617      */
4618     getDataIndex : function(col){
4619         return this.config[col].dataIndex;
4620     },
4621
4622     /**
4623      * Sets the dataIndex for a column.
4624      * @param {Number} col The column index
4625      * @param {Number} dataIndex The new dataIndex
4626      */
4627     setDataIndex : function(col, dataIndex){
4628         this.config[col].dataIndex = dataIndex;
4629     },
4630
4631     
4632     
4633     /**
4634      * Returns true if the cell is editable.
4635      * @param {Number} colIndex The column index
4636      * @param {Number} rowIndex The row index
4637      * @return {Boolean}
4638      */
4639     isCellEditable : function(colIndex, rowIndex){
4640         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4641     },
4642
4643     /**
4644      * Returns the editor defined for the cell/column.
4645      * return false or null to disable editing.
4646      * @param {Number} colIndex The column index
4647      * @param {Number} rowIndex The row index
4648      * @return {Object}
4649      */
4650     getCellEditor : function(colIndex, rowIndex){
4651         return this.config[colIndex].editor;
4652     },
4653
4654     /**
4655      * Sets if a column is editable.
4656      * @param {Number} col The column index
4657      * @param {Boolean} editable True if the column is editable
4658      */
4659     setEditable : function(col, editable){
4660         this.config[col].editable = editable;
4661     },
4662
4663
4664     /**
4665      * Returns true if the column is hidden.
4666      * @param {Number} colIndex The column index
4667      * @return {Boolean}
4668      */
4669     isHidden : function(colIndex){
4670         return this.config[colIndex].hidden;
4671     },
4672
4673
4674     /**
4675      * Returns true if the column width cannot be changed
4676      */
4677     isFixed : function(colIndex){
4678         return this.config[colIndex].fixed;
4679     },
4680
4681     /**
4682      * Returns true if the column can be resized
4683      * @return {Boolean}
4684      */
4685     isResizable : function(colIndex){
4686         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4687     },
4688     /**
4689      * Sets if a column is hidden.
4690      * @param {Number} colIndex The column index
4691      * @param {Boolean} hidden True if the column is hidden
4692      */
4693     setHidden : function(colIndex, hidden){
4694         this.config[colIndex].hidden = hidden;
4695         this.totalWidth = null;
4696         this.fireEvent("hiddenchange", this, colIndex, hidden);
4697     },
4698
4699     /**
4700      * Sets the editor for a column.
4701      * @param {Number} col The column index
4702      * @param {Object} editor The editor object
4703      */
4704     setEditor : function(col, editor){
4705         this.config[col].editor = editor;
4706     }
4707 });
4708
4709 Roo.grid.ColumnModel.defaultRenderer = function(value){
4710         if(typeof value == "string" && value.length < 1){
4711             return "&#160;";
4712         }
4713         return value;
4714 };
4715
4716 // Alias for backwards compatibility
4717 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4718 /*
4719  * Based on:
4720  * Ext JS Library 1.1.1
4721  * Copyright(c) 2006-2007, Ext JS, LLC.
4722  *
4723  * Originally Released Under LGPL - original licence link has changed is not relivant.
4724  *
4725  * Fork - LGPL
4726  * <script type="text/javascript">
4727  */
4728  
4729 /**
4730  * @class Roo.LoadMask
4731  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4732  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4733  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4734  * element's UpdateManager load indicator and will be destroyed after the initial load.
4735  * @constructor
4736  * Create a new LoadMask
4737  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4738  * @param {Object} config The config object
4739  */
4740 Roo.LoadMask = function(el, config){
4741     this.el = Roo.get(el);
4742     Roo.apply(this, config);
4743     if(this.store){
4744         this.store.on('beforeload', this.onBeforeLoad, this);
4745         this.store.on('load', this.onLoad, this);
4746         this.store.on('loadexception', this.onLoadException, this);
4747         this.removeMask = false;
4748     }else{
4749         var um = this.el.getUpdateManager();
4750         um.showLoadIndicator = false; // disable the default indicator
4751         um.on('beforeupdate', this.onBeforeLoad, this);
4752         um.on('update', this.onLoad, this);
4753         um.on('failure', this.onLoad, this);
4754         this.removeMask = true;
4755     }
4756 };
4757
4758 Roo.LoadMask.prototype = {
4759     /**
4760      * @cfg {Boolean} removeMask
4761      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4762      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4763      */
4764     /**
4765      * @cfg {String} msg
4766      * The text to display in a centered loading message box (defaults to 'Loading...')
4767      */
4768     msg : 'Loading...',
4769     /**
4770      * @cfg {String} msgCls
4771      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4772      */
4773     msgCls : 'x-mask-loading',
4774
4775     /**
4776      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4777      * @type Boolean
4778      */
4779     disabled: false,
4780
4781     /**
4782      * Disables the mask to prevent it from being displayed
4783      */
4784     disable : function(){
4785        this.disabled = true;
4786     },
4787
4788     /**
4789      * Enables the mask so that it can be displayed
4790      */
4791     enable : function(){
4792         this.disabled = false;
4793     },
4794     
4795     onLoadException : function()
4796     {
4797         Roo.log(arguments);
4798         
4799         if (typeof(arguments[3]) != 'undefined') {
4800             Roo.MessageBox.alert("Error loading",arguments[3]);
4801         } 
4802         /*
4803         try {
4804             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4805                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4806             }   
4807         } catch(e) {
4808             
4809         }
4810         */
4811     
4812         
4813         
4814         this.el.unmask(this.removeMask);
4815     },
4816     // private
4817     onLoad : function()
4818     {
4819         this.el.unmask(this.removeMask);
4820     },
4821
4822     // private
4823     onBeforeLoad : function(){
4824         if(!this.disabled){
4825             this.el.mask(this.msg, this.msgCls);
4826         }
4827     },
4828
4829     // private
4830     destroy : function(){
4831         if(this.store){
4832             this.store.un('beforeload', this.onBeforeLoad, this);
4833             this.store.un('load', this.onLoad, this);
4834             this.store.un('loadexception', this.onLoadException, this);
4835         }else{
4836             var um = this.el.getUpdateManager();
4837             um.un('beforeupdate', this.onBeforeLoad, this);
4838             um.un('update', this.onLoad, this);
4839             um.un('failure', this.onLoad, this);
4840         }
4841     }
4842 };/*
4843  * - LGPL
4844  *
4845  * table
4846  * 
4847  */
4848
4849 /**
4850  * @class Roo.bootstrap.Table
4851  * @extends Roo.bootstrap.Component
4852  * Bootstrap Table class
4853  * @cfg {String} cls table class
4854  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4855  * @cfg {String} bgcolor Specifies the background color for a table
4856  * @cfg {Number} border Specifies whether the table cells should have borders or not
4857  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4858  * @cfg {Number} cellspacing Specifies the space between cells
4859  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4860  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4861  * @cfg {String} sortable Specifies that the table should be sortable
4862  * @cfg {String} summary Specifies a summary of the content of a table
4863  * @cfg {Number} width Specifies the width of a table
4864  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4865  * 
4866  * @cfg {boolean} striped Should the rows be alternative striped
4867  * @cfg {boolean} bordered Add borders to the table
4868  * @cfg {boolean} hover Add hover highlighting
4869  * @cfg {boolean} condensed Format condensed
4870  * @cfg {boolean} responsive Format condensed
4871  * @cfg {Boolean} loadMask (true|false) default false
4872  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4873  * @cfg {Boolean} thead (true|false) generate thead, default true
4874  * @cfg {Boolean} RowSelection (true|false) default false
4875  * @cfg {Boolean} CellSelection (true|false) default false
4876  *
4877  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4878  
4879  * 
4880  * @constructor
4881  * Create a new Table
4882  * @param {Object} config The config object
4883  */
4884
4885 Roo.bootstrap.Table = function(config){
4886     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4887     
4888     if (this.sm) {
4889         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4890         this.sm = this.selModel;
4891         this.sm.xmodule = this.xmodule || false;
4892     }
4893     if (this.cm && typeof(this.cm.config) == 'undefined') {
4894         this.colModel = new Roo.grid.ColumnModel(this.cm);
4895         this.cm = this.colModel;
4896         this.cm.xmodule = this.xmodule || false;
4897     }
4898     if (this.store) {
4899         this.store= Roo.factory(this.store, Roo.data);
4900         this.ds = this.store;
4901         this.ds.xmodule = this.xmodule || false;
4902          
4903     }
4904     if (this.footer && this.store) {
4905         this.footer.dataSource = this.ds;
4906         this.footer = Roo.factory(this.footer);
4907     }
4908     
4909     /** @private */
4910     this.addEvents({
4911         /**
4912          * @event cellclick
4913          * Fires when a cell is clicked
4914          * @param {Roo.bootstrap.Table} this
4915          * @param {Roo.Element} el
4916          * @param {Number} rowIndex
4917          * @param {Number} columnIndex
4918          * @param {Roo.EventObject} e
4919          */
4920         "cellclick" : true,
4921         /**
4922          * @event celldblclick
4923          * Fires when a cell is double clicked
4924          * @param {Roo.bootstrap.Table} this
4925          * @param {Roo.Element} el
4926          * @param {Number} rowIndex
4927          * @param {Number} columnIndex
4928          * @param {Roo.EventObject} e
4929          */
4930         "celldblclick" : true,
4931         /**
4932          * @event rowclick
4933          * Fires when a row is clicked
4934          * @param {Roo.bootstrap.Table} this
4935          * @param {Roo.Element} el
4936          * @param {Number} rowIndex
4937          * @param {Roo.EventObject} e
4938          */
4939         "rowclick" : true,
4940         /**
4941          * @event rowdblclick
4942          * Fires when a row is double clicked
4943          * @param {Roo.bootstrap.Table} this
4944          * @param {Roo.Element} el
4945          * @param {Number} rowIndex
4946          * @param {Roo.EventObject} e
4947          */
4948         "rowdblclick" : true,
4949         /**
4950          * @event mouseover
4951          * Fires when a mouseover occur
4952          * @param {Roo.bootstrap.Table} this
4953          * @param {Roo.Element} el
4954          * @param {Number} rowIndex
4955          * @param {Number} columnIndex
4956          * @param {Roo.EventObject} e
4957          */
4958         "mouseover" : true,
4959         /**
4960          * @event mouseout
4961          * Fires when a mouseout occur
4962          * @param {Roo.bootstrap.Table} this
4963          * @param {Roo.Element} el
4964          * @param {Number} rowIndex
4965          * @param {Number} columnIndex
4966          * @param {Roo.EventObject} e
4967          */
4968         "mouseout" : true,
4969         /**
4970          * @event rowclass
4971          * Fires when a row is rendered, so you can change add a style to it.
4972          * @param {Roo.bootstrap.Table} this
4973          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4974          */
4975         'rowclass' : true
4976         
4977     });
4978 };
4979
4980 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4981     
4982     cls: false,
4983     align: false,
4984     bgcolor: false,
4985     border: false,
4986     cellpadding: false,
4987     cellspacing: false,
4988     frame: false,
4989     rules: false,
4990     sortable: false,
4991     summary: false,
4992     width: false,
4993     striped : false,
4994     bordered: false,
4995     hover:  false,
4996     condensed : false,
4997     responsive : false,
4998     sm : false,
4999     cm : false,
5000     store : false,
5001     loadMask : false,
5002     tfoot : true,
5003     thead : true,
5004     RowSelection : false,
5005     CellSelection : false,
5006     layout : false,
5007     
5008     // Roo.Element - the tbody
5009     mainBody: false, 
5010     
5011     getAutoCreate : function(){
5012         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5013         
5014         cfg = {
5015             tag: 'table',
5016             cls : 'table',
5017             cn : []
5018         }
5019             
5020         if (this.striped) {
5021             cfg.cls += ' table-striped';
5022         }
5023         
5024         if (this.hover) {
5025             cfg.cls += ' table-hover';
5026         }
5027         if (this.bordered) {
5028             cfg.cls += ' table-bordered';
5029         }
5030         if (this.condensed) {
5031             cfg.cls += ' table-condensed';
5032         }
5033         if (this.responsive) {
5034             cfg.cls += ' table-responsive';
5035         }
5036         
5037         if (this.cls) {
5038             cfg.cls+=  ' ' +this.cls;
5039         }
5040         
5041         // this lot should be simplifed...
5042         
5043         if (this.align) {
5044             cfg.align=this.align;
5045         }
5046         if (this.bgcolor) {
5047             cfg.bgcolor=this.bgcolor;
5048         }
5049         if (this.border) {
5050             cfg.border=this.border;
5051         }
5052         if (this.cellpadding) {
5053             cfg.cellpadding=this.cellpadding;
5054         }
5055         if (this.cellspacing) {
5056             cfg.cellspacing=this.cellspacing;
5057         }
5058         if (this.frame) {
5059             cfg.frame=this.frame;
5060         }
5061         if (this.rules) {
5062             cfg.rules=this.rules;
5063         }
5064         if (this.sortable) {
5065             cfg.sortable=this.sortable;
5066         }
5067         if (this.summary) {
5068             cfg.summary=this.summary;
5069         }
5070         if (this.width) {
5071             cfg.width=this.width;
5072         }
5073         if (this.layout) {
5074             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5075         }
5076         
5077         if(this.store || this.cm){
5078             if(this.thead){
5079                 cfg.cn.push(this.renderHeader());
5080             }
5081             
5082             cfg.cn.push(this.renderBody());
5083             
5084             if(this.tfoot){
5085                 cfg.cn.push(this.renderFooter());
5086             }
5087             
5088             cfg.cls+=  ' TableGrid';
5089         }
5090         
5091         return { cn : [ cfg ] };
5092     },
5093     
5094     initEvents : function()
5095     {   
5096         if(!this.store || !this.cm){
5097             return;
5098         }
5099         
5100         //Roo.log('initEvents with ds!!!!');
5101         
5102         this.mainBody = this.el.select('tbody', true).first();
5103         
5104         
5105         var _this = this;
5106         
5107         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5108             e.on('click', _this.sort, _this);
5109         });
5110         
5111         this.el.on("click", this.onClick, this);
5112         this.el.on("dblclick", this.onDblClick, this);
5113         
5114         this.parent().el.setStyle('position', 'relative');
5115         if (this.footer) {
5116             this.footer.parentId = this.id;
5117             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5118         }
5119         
5120         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5121         
5122         this.store.on('load', this.onLoad, this);
5123         this.store.on('beforeload', this.onBeforeLoad, this);
5124         this.store.on('update', this.onUpdate, this);
5125         
5126     },
5127     
5128     onMouseover : function(e, el)
5129     {
5130         var cell = Roo.get(el);
5131         
5132         if(!cell){
5133             return;
5134         }
5135         
5136         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5137             cell = cell.findParent('td', false, true);
5138         }
5139         
5140         var row = cell.findParent('tr', false, true);
5141         var cellIndex = cell.dom.cellIndex;
5142         var rowIndex = row.dom.rowIndex - 1; // start from 0
5143         
5144         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5145         
5146     },
5147     
5148     onMouseout : function(e, el)
5149     {
5150         var cell = Roo.get(el);
5151         
5152         if(!cell){
5153             return;
5154         }
5155         
5156         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5157             cell = cell.findParent('td', false, true);
5158         }
5159         
5160         var row = cell.findParent('tr', false, true);
5161         var cellIndex = cell.dom.cellIndex;
5162         var rowIndex = row.dom.rowIndex - 1; // start from 0
5163         
5164         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5165         
5166     },
5167     
5168     onClick : function(e, el)
5169     {
5170         var cell = Roo.get(el);
5171         
5172         if(!cell || (!this.CellSelection && !this.RowSelection)){
5173             return;
5174         }
5175         
5176         
5177         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5178             cell = cell.findParent('td', false, true);
5179         }
5180         
5181         var row = cell.findParent('tr', false, true);
5182         var cellIndex = cell.dom.cellIndex;
5183         var rowIndex = row.dom.rowIndex - 1;
5184         
5185         if(this.CellSelection){
5186             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5187         }
5188         
5189         if(this.RowSelection){
5190             this.fireEvent('rowclick', this, row, rowIndex, e);
5191         }
5192         
5193         
5194     },
5195     
5196     onDblClick : function(e,el)
5197     {
5198         var cell = Roo.get(el);
5199         
5200         if(!cell || (!this.CellSelection && !this.RowSelection)){
5201             return;
5202         }
5203         
5204         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5205             cell = cell.findParent('td', false, true);
5206         }
5207         
5208         var row = cell.findParent('tr', false, true);
5209         var cellIndex = cell.dom.cellIndex;
5210         var rowIndex = row.dom.rowIndex - 1;
5211         
5212         if(this.CellSelection){
5213             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5214         }
5215         
5216         if(this.RowSelection){
5217             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5218         }
5219     },
5220     
5221     sort : function(e,el)
5222     {
5223         var col = Roo.get(el)
5224         
5225         if(!col.hasClass('sortable')){
5226             return;
5227         }
5228         
5229         var sort = col.attr('sort');
5230         var dir = 'ASC';
5231         
5232         if(col.hasClass('glyphicon-arrow-up')){
5233             dir = 'DESC';
5234         }
5235         
5236         this.store.sortInfo = {field : sort, direction : dir};
5237         
5238         if (this.footer) {
5239             Roo.log("calling footer first");
5240             this.footer.onClick('first');
5241         } else {
5242         
5243             this.store.load({ params : { start : 0 } });
5244         }
5245     },
5246     
5247     renderHeader : function()
5248     {
5249         var header = {
5250             tag: 'thead',
5251             cn : []
5252         };
5253         
5254         var cm = this.cm;
5255         
5256         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5257             
5258             var config = cm.config[i];
5259                     
5260             var c = {
5261                 tag: 'th',
5262                 style : '',
5263                 html: cm.getColumnHeader(i)
5264             };
5265             
5266             if(typeof(config.hidden) != 'undefined' && config.hidden){
5267                 c.style += ' display:none;';
5268             }
5269             
5270             if(typeof(config.dataIndex) != 'undefined'){
5271                 c.sort = config.dataIndex;
5272             }
5273             
5274             if(typeof(config.sortable) != 'undefined' && config.sortable){
5275                 c.cls = 'sortable';
5276             }
5277             
5278             if(typeof(config.align) != 'undefined' && config.align.length){
5279                 c.style += ' text-align:' + config.align + ';';
5280             }
5281             
5282             if(typeof(config.width) != 'undefined'){
5283                 c.style += ' width:' + config.width + 'px;';
5284             }
5285             
5286             header.cn.push(c)
5287         }
5288         
5289         return header;
5290     },
5291     
5292     renderBody : function()
5293     {
5294         var body = {
5295             tag: 'tbody',
5296             cn : [
5297                 {
5298                     tag: 'tr',
5299                     cn : [
5300                         {
5301                             tag : 'td',
5302                             colspan :  this.cm.getColumnCount()
5303                         }
5304                     ]
5305                 }
5306             ]
5307         };
5308         
5309         return body;
5310     },
5311     
5312     renderFooter : function()
5313     {
5314         var footer = {
5315             tag: 'tfoot',
5316             cn : [
5317                 {
5318                     tag: 'tr',
5319                     cn : [
5320                         {
5321                             tag : 'td',
5322                             colspan :  this.cm.getColumnCount()
5323                         }
5324                     ]
5325                 }
5326             ]
5327         };
5328         
5329         return footer;
5330     },
5331     
5332     
5333     
5334     onLoad : function()
5335     {
5336         Roo.log('ds onload');
5337         this.clear();
5338         
5339         var _this = this;
5340         var cm = this.cm;
5341         var ds = this.store;
5342         
5343         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5344             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5345             
5346             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5347                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5348             }
5349             
5350             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5351                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5352             }
5353         });
5354         
5355         var tbody =  this.mainBody;
5356               
5357         if(ds.getCount() > 0){
5358             ds.data.each(function(d,rowIndex){
5359                 var row =  this.renderRow(cm, ds, rowIndex);
5360                 
5361                 tbody.createChild(row);
5362                 
5363                 var _this = this;
5364                 
5365                 if(row.cellObjects.length){
5366                     Roo.each(row.cellObjects, function(r){
5367                         _this.renderCellObject(r);
5368                     })
5369                 }
5370                 
5371             }, this);
5372         }
5373         
5374         Roo.each(this.el.select('tbody td', true).elements, function(e){
5375             e.on('mouseover', _this.onMouseover, _this);
5376         });
5377         
5378         Roo.each(this.el.select('tbody td', true).elements, function(e){
5379             e.on('mouseout', _this.onMouseout, _this);
5380         });
5381
5382         //if(this.loadMask){
5383         //    this.maskEl.hide();
5384         //}
5385     },
5386     
5387     
5388     onUpdate : function(ds,record)
5389     {
5390         this.refreshRow(record);
5391     },
5392     onRemove : function(ds, record, index, isUpdate){
5393         if(isUpdate !== true){
5394             this.fireEvent("beforerowremoved", this, index, record);
5395         }
5396         var bt = this.mainBody.dom;
5397         if(bt.rows[index]){
5398             bt.removeChild(bt.rows[index]);
5399         }
5400         
5401         if(isUpdate !== true){
5402             //this.stripeRows(index);
5403             //this.syncRowHeights(index, index);
5404             //this.layout();
5405             this.fireEvent("rowremoved", this, index, record);
5406         }
5407     },
5408     
5409     
5410     refreshRow : function(record){
5411         var ds = this.store, index;
5412         if(typeof record == 'number'){
5413             index = record;
5414             record = ds.getAt(index);
5415         }else{
5416             index = ds.indexOf(record);
5417         }
5418         this.insertRow(ds, index, true);
5419         this.onRemove(ds, record, index+1, true);
5420         //this.syncRowHeights(index, index);
5421         //this.layout();
5422         this.fireEvent("rowupdated", this, index, record);
5423     },
5424     
5425     insertRow : function(dm, rowIndex, isUpdate){
5426         
5427         if(!isUpdate){
5428             this.fireEvent("beforerowsinserted", this, rowIndex);
5429         }
5430             //var s = this.getScrollState();
5431         var row = this.renderRow(this.cm, this.store, rowIndex);
5432         // insert before rowIndex..
5433         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5434         
5435         var _this = this;
5436                 
5437         if(row.cellObjects.length){
5438             Roo.each(row.cellObjects, function(r){
5439                 _this.renderCellObject(r);
5440             })
5441         }
5442             
5443         if(!isUpdate){
5444             this.fireEvent("rowsinserted", this, rowIndex);
5445             //this.syncRowHeights(firstRow, lastRow);
5446             //this.stripeRows(firstRow);
5447             //this.layout();
5448         }
5449         
5450     },
5451     
5452     
5453     getRowDom : function(rowIndex)
5454     {
5455         // not sure if I need to check this.. but let's do it anyway..
5456         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5457                 this.mainBody.dom.rows[rowIndex] : false
5458     },
5459     // returns the object tree for a tr..
5460   
5461     
5462     renderRow : function(cm, ds, rowIndex) {
5463         
5464         var d = ds.getAt(rowIndex);
5465         
5466         var row = {
5467             tag : 'tr',
5468             cn : []
5469         };
5470             
5471         var cellObjects = [];
5472         
5473         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5474             var config = cm.config[i];
5475             
5476             var renderer = cm.getRenderer(i);
5477             var value = '';
5478             var id = false;
5479             
5480             if(typeof(renderer) !== 'undefined'){
5481                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5482             }
5483             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5484             // and are rendered into the cells after the row is rendered - using the id for the element.
5485             
5486             if(typeof(value) === 'object'){
5487                 id = Roo.id();
5488                 cellObjects.push({
5489                     container : id,
5490                     cfg : value 
5491                 })
5492             }
5493             
5494             var rowcfg = {
5495                 record: d,
5496                 rowIndex : rowIndex,
5497                 colIndex : i,
5498                 rowClass : ''
5499             }
5500
5501             this.fireEvent('rowclass', this, rowcfg);
5502             
5503             var td = {
5504                 tag: 'td',
5505                 cls : rowcfg.rowClass,
5506                 style: '',
5507                 html: (typeof(value) === 'object') ? '' : value
5508             };
5509             
5510             if (id) {
5511                 td.id = id;
5512             }
5513             
5514             if(typeof(config.hidden) != 'undefined' && config.hidden){
5515                 td.style += ' display:none;';
5516             }
5517             
5518             if(typeof(config.align) != 'undefined' && config.align.length){
5519                 td.style += ' text-align:' + config.align + ';';
5520             }
5521             
5522             if(typeof(config.width) != 'undefined'){
5523                 td.style += ' width:' +  config.width + 'px;';
5524             }
5525              
5526             row.cn.push(td);
5527            
5528         }
5529         
5530         row.cellObjects = cellObjects;
5531         
5532         return row;
5533           
5534     },
5535     
5536     
5537     
5538     onBeforeLoad : function()
5539     {
5540         //Roo.log('ds onBeforeLoad');
5541         
5542         //this.clear();
5543         
5544         //if(this.loadMask){
5545         //    this.maskEl.show();
5546         //}
5547     },
5548     
5549     clear : function()
5550     {
5551         this.el.select('tbody', true).first().dom.innerHTML = '';
5552     },
5553     
5554     getSelectionModel : function(){
5555         if(!this.selModel){
5556             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5557         }
5558         return this.selModel;
5559     },
5560     /*
5561      * Render the Roo.bootstrap object from renderder
5562      */
5563     renderCellObject : function(r)
5564     {
5565         var _this = this;
5566         
5567         var t = r.cfg.render(r.container);
5568         
5569         if(r.cfg.cn){
5570             Roo.each(r.cfg.cn, function(c){
5571                 var child = {
5572                     container: t.getChildContainer(),
5573                     cfg: c
5574                 }
5575                 _this.renderCellObject(child);
5576             })
5577         }
5578     }
5579    
5580 });
5581
5582  
5583
5584  /*
5585  * - LGPL
5586  *
5587  * table cell
5588  * 
5589  */
5590
5591 /**
5592  * @class Roo.bootstrap.TableCell
5593  * @extends Roo.bootstrap.Component
5594  * Bootstrap TableCell class
5595  * @cfg {String} html cell contain text
5596  * @cfg {String} cls cell class
5597  * @cfg {String} tag cell tag (td|th) default td
5598  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5599  * @cfg {String} align Aligns the content in a cell
5600  * @cfg {String} axis Categorizes cells
5601  * @cfg {String} bgcolor Specifies the background color of a cell
5602  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5603  * @cfg {Number} colspan Specifies the number of columns a cell should span
5604  * @cfg {String} headers Specifies one or more header cells a cell is related to
5605  * @cfg {Number} height Sets the height of a cell
5606  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5607  * @cfg {Number} rowspan Sets the number of rows a cell should span
5608  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5609  * @cfg {String} valign Vertical aligns the content in a cell
5610  * @cfg {Number} width Specifies the width of a cell
5611  * 
5612  * @constructor
5613  * Create a new TableCell
5614  * @param {Object} config The config object
5615  */
5616
5617 Roo.bootstrap.TableCell = function(config){
5618     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5619 };
5620
5621 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5622     
5623     html: false,
5624     cls: false,
5625     tag: false,
5626     abbr: false,
5627     align: false,
5628     axis: false,
5629     bgcolor: false,
5630     charoff: false,
5631     colspan: false,
5632     headers: false,
5633     height: false,
5634     nowrap: false,
5635     rowspan: false,
5636     scope: false,
5637     valign: false,
5638     width: false,
5639     
5640     
5641     getAutoCreate : function(){
5642         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5643         
5644         cfg = {
5645             tag: 'td'
5646         }
5647         
5648         if(this.tag){
5649             cfg.tag = this.tag;
5650         }
5651         
5652         if (this.html) {
5653             cfg.html=this.html
5654         }
5655         if (this.cls) {
5656             cfg.cls=this.cls
5657         }
5658         if (this.abbr) {
5659             cfg.abbr=this.abbr
5660         }
5661         if (this.align) {
5662             cfg.align=this.align
5663         }
5664         if (this.axis) {
5665             cfg.axis=this.axis
5666         }
5667         if (this.bgcolor) {
5668             cfg.bgcolor=this.bgcolor
5669         }
5670         if (this.charoff) {
5671             cfg.charoff=this.charoff
5672         }
5673         if (this.colspan) {
5674             cfg.colspan=this.colspan
5675         }
5676         if (this.headers) {
5677             cfg.headers=this.headers
5678         }
5679         if (this.height) {
5680             cfg.height=this.height
5681         }
5682         if (this.nowrap) {
5683             cfg.nowrap=this.nowrap
5684         }
5685         if (this.rowspan) {
5686             cfg.rowspan=this.rowspan
5687         }
5688         if (this.scope) {
5689             cfg.scope=this.scope
5690         }
5691         if (this.valign) {
5692             cfg.valign=this.valign
5693         }
5694         if (this.width) {
5695             cfg.width=this.width
5696         }
5697         
5698         
5699         return cfg;
5700     }
5701    
5702 });
5703
5704  
5705
5706  /*
5707  * - LGPL
5708  *
5709  * table row
5710  * 
5711  */
5712
5713 /**
5714  * @class Roo.bootstrap.TableRow
5715  * @extends Roo.bootstrap.Component
5716  * Bootstrap TableRow class
5717  * @cfg {String} cls row class
5718  * @cfg {String} align Aligns the content in a table row
5719  * @cfg {String} bgcolor Specifies a background color for a table row
5720  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5721  * @cfg {String} valign Vertical aligns the content in a table row
5722  * 
5723  * @constructor
5724  * Create a new TableRow
5725  * @param {Object} config The config object
5726  */
5727
5728 Roo.bootstrap.TableRow = function(config){
5729     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5730 };
5731
5732 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5733     
5734     cls: false,
5735     align: false,
5736     bgcolor: false,
5737     charoff: false,
5738     valign: false,
5739     
5740     getAutoCreate : function(){
5741         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5742         
5743         cfg = {
5744             tag: 'tr'
5745         }
5746             
5747         if(this.cls){
5748             cfg.cls = this.cls;
5749         }
5750         if(this.align){
5751             cfg.align = this.align;
5752         }
5753         if(this.bgcolor){
5754             cfg.bgcolor = this.bgcolor;
5755         }
5756         if(this.charoff){
5757             cfg.charoff = this.charoff;
5758         }
5759         if(this.valign){
5760             cfg.valign = this.valign;
5761         }
5762         
5763         return cfg;
5764     }
5765    
5766 });
5767
5768  
5769
5770  /*
5771  * - LGPL
5772  *
5773  * table body
5774  * 
5775  */
5776
5777 /**
5778  * @class Roo.bootstrap.TableBody
5779  * @extends Roo.bootstrap.Component
5780  * Bootstrap TableBody class
5781  * @cfg {String} cls element class
5782  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5783  * @cfg {String} align Aligns the content inside the element
5784  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5785  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5786  * 
5787  * @constructor
5788  * Create a new TableBody
5789  * @param {Object} config The config object
5790  */
5791
5792 Roo.bootstrap.TableBody = function(config){
5793     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5794 };
5795
5796 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5797     
5798     cls: false,
5799     tag: false,
5800     align: false,
5801     charoff: false,
5802     valign: false,
5803     
5804     getAutoCreate : function(){
5805         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5806         
5807         cfg = {
5808             tag: 'tbody'
5809         }
5810             
5811         if (this.cls) {
5812             cfg.cls=this.cls
5813         }
5814         if(this.tag){
5815             cfg.tag = this.tag;
5816         }
5817         
5818         if(this.align){
5819             cfg.align = this.align;
5820         }
5821         if(this.charoff){
5822             cfg.charoff = this.charoff;
5823         }
5824         if(this.valign){
5825             cfg.valign = this.valign;
5826         }
5827         
5828         return cfg;
5829     }
5830     
5831     
5832 //    initEvents : function()
5833 //    {
5834 //        
5835 //        if(!this.store){
5836 //            return;
5837 //        }
5838 //        
5839 //        this.store = Roo.factory(this.store, Roo.data);
5840 //        this.store.on('load', this.onLoad, this);
5841 //        
5842 //        this.store.load();
5843 //        
5844 //    },
5845 //    
5846 //    onLoad: function () 
5847 //    {   
5848 //        this.fireEvent('load', this);
5849 //    }
5850 //    
5851 //   
5852 });
5853
5854  
5855
5856  /*
5857  * Based on:
5858  * Ext JS Library 1.1.1
5859  * Copyright(c) 2006-2007, Ext JS, LLC.
5860  *
5861  * Originally Released Under LGPL - original licence link has changed is not relivant.
5862  *
5863  * Fork - LGPL
5864  * <script type="text/javascript">
5865  */
5866
5867 // as we use this in bootstrap.
5868 Roo.namespace('Roo.form');
5869  /**
5870  * @class Roo.form.Action
5871  * Internal Class used to handle form actions
5872  * @constructor
5873  * @param {Roo.form.BasicForm} el The form element or its id
5874  * @param {Object} config Configuration options
5875  */
5876
5877  
5878  
5879 // define the action interface
5880 Roo.form.Action = function(form, options){
5881     this.form = form;
5882     this.options = options || {};
5883 };
5884 /**
5885  * Client Validation Failed
5886  * @const 
5887  */
5888 Roo.form.Action.CLIENT_INVALID = 'client';
5889 /**
5890  * Server Validation Failed
5891  * @const 
5892  */
5893 Roo.form.Action.SERVER_INVALID = 'server';
5894  /**
5895  * Connect to Server Failed
5896  * @const 
5897  */
5898 Roo.form.Action.CONNECT_FAILURE = 'connect';
5899 /**
5900  * Reading Data from Server Failed
5901  * @const 
5902  */
5903 Roo.form.Action.LOAD_FAILURE = 'load';
5904
5905 Roo.form.Action.prototype = {
5906     type : 'default',
5907     failureType : undefined,
5908     response : undefined,
5909     result : undefined,
5910
5911     // interface method
5912     run : function(options){
5913
5914     },
5915
5916     // interface method
5917     success : function(response){
5918
5919     },
5920
5921     // interface method
5922     handleResponse : function(response){
5923
5924     },
5925
5926     // default connection failure
5927     failure : function(response){
5928         
5929         this.response = response;
5930         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5931         this.form.afterAction(this, false);
5932     },
5933
5934     processResponse : function(response){
5935         this.response = response;
5936         if(!response.responseText){
5937             return true;
5938         }
5939         this.result = this.handleResponse(response);
5940         return this.result;
5941     },
5942
5943     // utility functions used internally
5944     getUrl : function(appendParams){
5945         var url = this.options.url || this.form.url || this.form.el.dom.action;
5946         if(appendParams){
5947             var p = this.getParams();
5948             if(p){
5949                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5950             }
5951         }
5952         return url;
5953     },
5954
5955     getMethod : function(){
5956         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5957     },
5958
5959     getParams : function(){
5960         var bp = this.form.baseParams;
5961         var p = this.options.params;
5962         if(p){
5963             if(typeof p == "object"){
5964                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5965             }else if(typeof p == 'string' && bp){
5966                 p += '&' + Roo.urlEncode(bp);
5967             }
5968         }else if(bp){
5969             p = Roo.urlEncode(bp);
5970         }
5971         return p;
5972     },
5973
5974     createCallback : function(){
5975         return {
5976             success: this.success,
5977             failure: this.failure,
5978             scope: this,
5979             timeout: (this.form.timeout*1000),
5980             upload: this.form.fileUpload ? this.success : undefined
5981         };
5982     }
5983 };
5984
5985 Roo.form.Action.Submit = function(form, options){
5986     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5987 };
5988
5989 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5990     type : 'submit',
5991
5992     haveProgress : false,
5993     uploadComplete : false,
5994     
5995     // uploadProgress indicator.
5996     uploadProgress : function()
5997     {
5998         if (!this.form.progressUrl) {
5999             return;
6000         }
6001         
6002         if (!this.haveProgress) {
6003             Roo.MessageBox.progress("Uploading", "Uploading");
6004         }
6005         if (this.uploadComplete) {
6006            Roo.MessageBox.hide();
6007            return;
6008         }
6009         
6010         this.haveProgress = true;
6011    
6012         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6013         
6014         var c = new Roo.data.Connection();
6015         c.request({
6016             url : this.form.progressUrl,
6017             params: {
6018                 id : uid
6019             },
6020             method: 'GET',
6021             success : function(req){
6022                //console.log(data);
6023                 var rdata = false;
6024                 var edata;
6025                 try  {
6026                    rdata = Roo.decode(req.responseText)
6027                 } catch (e) {
6028                     Roo.log("Invalid data from server..");
6029                     Roo.log(edata);
6030                     return;
6031                 }
6032                 if (!rdata || !rdata.success) {
6033                     Roo.log(rdata);
6034                     Roo.MessageBox.alert(Roo.encode(rdata));
6035                     return;
6036                 }
6037                 var data = rdata.data;
6038                 
6039                 if (this.uploadComplete) {
6040                    Roo.MessageBox.hide();
6041                    return;
6042                 }
6043                    
6044                 if (data){
6045                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6046                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6047                     );
6048                 }
6049                 this.uploadProgress.defer(2000,this);
6050             },
6051        
6052             failure: function(data) {
6053                 Roo.log('progress url failed ');
6054                 Roo.log(data);
6055             },
6056             scope : this
6057         });
6058            
6059     },
6060     
6061     
6062     run : function()
6063     {
6064         // run get Values on the form, so it syncs any secondary forms.
6065         this.form.getValues();
6066         
6067         var o = this.options;
6068         var method = this.getMethod();
6069         var isPost = method == 'POST';
6070         if(o.clientValidation === false || this.form.isValid()){
6071             
6072             if (this.form.progressUrl) {
6073                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6074                     (new Date() * 1) + '' + Math.random());
6075                     
6076             } 
6077             
6078             
6079             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6080                 form:this.form.el.dom,
6081                 url:this.getUrl(!isPost),
6082                 method: method,
6083                 params:isPost ? this.getParams() : null,
6084                 isUpload: this.form.fileUpload
6085             }));
6086             
6087             this.uploadProgress();
6088
6089         }else if (o.clientValidation !== false){ // client validation failed
6090             this.failureType = Roo.form.Action.CLIENT_INVALID;
6091             this.form.afterAction(this, false);
6092         }
6093     },
6094
6095     success : function(response)
6096     {
6097         this.uploadComplete= true;
6098         if (this.haveProgress) {
6099             Roo.MessageBox.hide();
6100         }
6101         
6102         
6103         var result = this.processResponse(response);
6104         if(result === true || result.success){
6105             this.form.afterAction(this, true);
6106             return;
6107         }
6108         if(result.errors){
6109             this.form.markInvalid(result.errors);
6110             this.failureType = Roo.form.Action.SERVER_INVALID;
6111         }
6112         this.form.afterAction(this, false);
6113     },
6114     failure : function(response)
6115     {
6116         this.uploadComplete= true;
6117         if (this.haveProgress) {
6118             Roo.MessageBox.hide();
6119         }
6120         
6121         this.response = response;
6122         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6123         this.form.afterAction(this, false);
6124     },
6125     
6126     handleResponse : function(response){
6127         if(this.form.errorReader){
6128             var rs = this.form.errorReader.read(response);
6129             var errors = [];
6130             if(rs.records){
6131                 for(var i = 0, len = rs.records.length; i < len; i++) {
6132                     var r = rs.records[i];
6133                     errors[i] = r.data;
6134                 }
6135             }
6136             if(errors.length < 1){
6137                 errors = null;
6138             }
6139             return {
6140                 success : rs.success,
6141                 errors : errors
6142             };
6143         }
6144         var ret = false;
6145         try {
6146             ret = Roo.decode(response.responseText);
6147         } catch (e) {
6148             ret = {
6149                 success: false,
6150                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6151                 errors : []
6152             };
6153         }
6154         return ret;
6155         
6156     }
6157 });
6158
6159
6160 Roo.form.Action.Load = function(form, options){
6161     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6162     this.reader = this.form.reader;
6163 };
6164
6165 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6166     type : 'load',
6167
6168     run : function(){
6169         
6170         Roo.Ajax.request(Roo.apply(
6171                 this.createCallback(), {
6172                     method:this.getMethod(),
6173                     url:this.getUrl(false),
6174                     params:this.getParams()
6175         }));
6176     },
6177
6178     success : function(response){
6179         
6180         var result = this.processResponse(response);
6181         if(result === true || !result.success || !result.data){
6182             this.failureType = Roo.form.Action.LOAD_FAILURE;
6183             this.form.afterAction(this, false);
6184             return;
6185         }
6186         this.form.clearInvalid();
6187         this.form.setValues(result.data);
6188         this.form.afterAction(this, true);
6189     },
6190
6191     handleResponse : function(response){
6192         if(this.form.reader){
6193             var rs = this.form.reader.read(response);
6194             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6195             return {
6196                 success : rs.success,
6197                 data : data
6198             };
6199         }
6200         return Roo.decode(response.responseText);
6201     }
6202 });
6203
6204 Roo.form.Action.ACTION_TYPES = {
6205     'load' : Roo.form.Action.Load,
6206     'submit' : Roo.form.Action.Submit
6207 };/*
6208  * - LGPL
6209  *
6210  * form
6211  * 
6212  */
6213
6214 /**
6215  * @class Roo.bootstrap.Form
6216  * @extends Roo.bootstrap.Component
6217  * Bootstrap Form class
6218  * @cfg {String} method  GET | POST (default POST)
6219  * @cfg {String} labelAlign top | left (default top)
6220  * @cfg {String} align left  | right - for navbars
6221  * @cfg {Boolean} loadMask load mask when submit (default true)
6222
6223  * 
6224  * @constructor
6225  * Create a new Form
6226  * @param {Object} config The config object
6227  */
6228
6229
6230 Roo.bootstrap.Form = function(config){
6231     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6232     this.addEvents({
6233         /**
6234          * @event clientvalidation
6235          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6236          * @param {Form} this
6237          * @param {Boolean} valid true if the form has passed client-side validation
6238          */
6239         clientvalidation: true,
6240         /**
6241          * @event beforeaction
6242          * Fires before any action is performed. Return false to cancel the action.
6243          * @param {Form} this
6244          * @param {Action} action The action to be performed
6245          */
6246         beforeaction: true,
6247         /**
6248          * @event actionfailed
6249          * Fires when an action fails.
6250          * @param {Form} this
6251          * @param {Action} action The action that failed
6252          */
6253         actionfailed : true,
6254         /**
6255          * @event actioncomplete
6256          * Fires when an action is completed.
6257          * @param {Form} this
6258          * @param {Action} action The action that completed
6259          */
6260         actioncomplete : true
6261     });
6262     
6263 };
6264
6265 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6266       
6267      /**
6268      * @cfg {String} method
6269      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6270      */
6271     method : 'POST',
6272     /**
6273      * @cfg {String} url
6274      * The URL to use for form actions if one isn't supplied in the action options.
6275      */
6276     /**
6277      * @cfg {Boolean} fileUpload
6278      * Set to true if this form is a file upload.
6279      */
6280      
6281     /**
6282      * @cfg {Object} baseParams
6283      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6284      */
6285       
6286     /**
6287      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6288      */
6289     timeout: 30,
6290     /**
6291      * @cfg {Sting} align (left|right) for navbar forms
6292      */
6293     align : 'left',
6294
6295     // private
6296     activeAction : null,
6297  
6298     /**
6299      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6300      * element by passing it or its id or mask the form itself by passing in true.
6301      * @type Mixed
6302      */
6303     waitMsgTarget : false,
6304     
6305     loadMask : true,
6306     
6307     getAutoCreate : function(){
6308         
6309         var cfg = {
6310             tag: 'form',
6311             method : this.method || 'POST',
6312             id : this.id || Roo.id(),
6313             cls : ''
6314         }
6315         if (this.parent().xtype.match(/^Nav/)) {
6316             cfg.cls = 'navbar-form navbar-' + this.align;
6317             
6318         }
6319         
6320         if (this.labelAlign == 'left' ) {
6321             cfg.cls += ' form-horizontal';
6322         }
6323         
6324         
6325         return cfg;
6326     },
6327     initEvents : function()
6328     {
6329         this.el.on('submit', this.onSubmit, this);
6330         // this was added as random key presses on the form where triggering form submit.
6331         this.el.on('keypress', function(e) {
6332             if (e.getCharCode() != 13) {
6333                 return true;
6334             }
6335             // we might need to allow it for textareas.. and some other items.
6336             // check e.getTarget().
6337             
6338             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6339                 return true;
6340             }
6341         
6342             Roo.log("keypress blocked");
6343             
6344             e.preventDefault();
6345             return false;
6346         });
6347         
6348     },
6349     // private
6350     onSubmit : function(e){
6351         e.stopEvent();
6352     },
6353     
6354      /**
6355      * Returns true if client-side validation on the form is successful.
6356      * @return Boolean
6357      */
6358     isValid : function(){
6359         var items = this.getItems();
6360         var valid = true;
6361         items.each(function(f){
6362            if(!f.validate()){
6363                valid = false;
6364                
6365            }
6366         });
6367         return valid;
6368     },
6369     /**
6370      * Returns true if any fields in this form have changed since their original load.
6371      * @return Boolean
6372      */
6373     isDirty : function(){
6374         var dirty = false;
6375         var items = this.getItems();
6376         items.each(function(f){
6377            if(f.isDirty()){
6378                dirty = true;
6379                return false;
6380            }
6381            return true;
6382         });
6383         return dirty;
6384     },
6385      /**
6386      * Performs a predefined action (submit or load) or custom actions you define on this form.
6387      * @param {String} actionName The name of the action type
6388      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6389      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6390      * accept other config options):
6391      * <pre>
6392 Property          Type             Description
6393 ----------------  ---------------  ----------------------------------------------------------------------------------
6394 url               String           The url for the action (defaults to the form's url)
6395 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6396 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6397 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6398                                    validate the form on the client (defaults to false)
6399      * </pre>
6400      * @return {BasicForm} this
6401      */
6402     doAction : function(action, options){
6403         if(typeof action == 'string'){
6404             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6405         }
6406         if(this.fireEvent('beforeaction', this, action) !== false){
6407             this.beforeAction(action);
6408             action.run.defer(100, action);
6409         }
6410         return this;
6411     },
6412     
6413     // private
6414     beforeAction : function(action){
6415         var o = action.options;
6416         
6417         if(this.loadMask){
6418             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6419         }
6420         // not really supported yet.. ??
6421         
6422         //if(this.waitMsgTarget === true){
6423         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6424         //}else if(this.waitMsgTarget){
6425         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6426         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6427         //}else {
6428         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6429        // }
6430          
6431     },
6432
6433     // private
6434     afterAction : function(action, success){
6435         this.activeAction = null;
6436         var o = action.options;
6437         
6438         //if(this.waitMsgTarget === true){
6439             this.el.unmask();
6440         //}else if(this.waitMsgTarget){
6441         //    this.waitMsgTarget.unmask();
6442         //}else{
6443         //    Roo.MessageBox.updateProgress(1);
6444         //    Roo.MessageBox.hide();
6445        // }
6446         // 
6447         if(success){
6448             if(o.reset){
6449                 this.reset();
6450             }
6451             Roo.callback(o.success, o.scope, [this, action]);
6452             this.fireEvent('actioncomplete', this, action);
6453             
6454         }else{
6455             
6456             // failure condition..
6457             // we have a scenario where updates need confirming.
6458             // eg. if a locking scenario exists..
6459             // we look for { errors : { needs_confirm : true }} in the response.
6460             if (
6461                 (typeof(action.result) != 'undefined')  &&
6462                 (typeof(action.result.errors) != 'undefined')  &&
6463                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6464            ){
6465                 var _t = this;
6466                 Roo.log("not supported yet");
6467                  /*
6468                 
6469                 Roo.MessageBox.confirm(
6470                     "Change requires confirmation",
6471                     action.result.errorMsg,
6472                     function(r) {
6473                         if (r != 'yes') {
6474                             return;
6475                         }
6476                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6477                     }
6478                     
6479                 );
6480                 */
6481                 
6482                 
6483                 return;
6484             }
6485             
6486             Roo.callback(o.failure, o.scope, [this, action]);
6487             // show an error message if no failed handler is set..
6488             if (!this.hasListener('actionfailed')) {
6489                 Roo.log("need to add dialog support");
6490                 /*
6491                 Roo.MessageBox.alert("Error",
6492                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6493                         action.result.errorMsg :
6494                         "Saving Failed, please check your entries or try again"
6495                 );
6496                 */
6497             }
6498             
6499             this.fireEvent('actionfailed', this, action);
6500         }
6501         
6502     },
6503     /**
6504      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6505      * @param {String} id The value to search for
6506      * @return Field
6507      */
6508     findField : function(id){
6509         var items = this.getItems();
6510         var field = items.get(id);
6511         if(!field){
6512              items.each(function(f){
6513                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6514                     field = f;
6515                     return false;
6516                 }
6517                 return true;
6518             });
6519         }
6520         return field || null;
6521     },
6522      /**
6523      * Mark fields in this form invalid in bulk.
6524      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6525      * @return {BasicForm} this
6526      */
6527     markInvalid : function(errors){
6528         if(errors instanceof Array){
6529             for(var i = 0, len = errors.length; i < len; i++){
6530                 var fieldError = errors[i];
6531                 var f = this.findField(fieldError.id);
6532                 if(f){
6533                     f.markInvalid(fieldError.msg);
6534                 }
6535             }
6536         }else{
6537             var field, id;
6538             for(id in errors){
6539                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6540                     field.markInvalid(errors[id]);
6541                 }
6542             }
6543         }
6544         //Roo.each(this.childForms || [], function (f) {
6545         //    f.markInvalid(errors);
6546         //});
6547         
6548         return this;
6549     },
6550
6551     /**
6552      * Set values for fields in this form in bulk.
6553      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6554      * @return {BasicForm} this
6555      */
6556     setValues : function(values){
6557         if(values instanceof Array){ // array of objects
6558             for(var i = 0, len = values.length; i < len; i++){
6559                 var v = values[i];
6560                 var f = this.findField(v.id);
6561                 if(f){
6562                     f.setValue(v.value);
6563                     if(this.trackResetOnLoad){
6564                         f.originalValue = f.getValue();
6565                     }
6566                 }
6567             }
6568         }else{ // object hash
6569             var field, id;
6570             for(id in values){
6571                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6572                     
6573                     if (field.setFromData && 
6574                         field.valueField && 
6575                         field.displayField &&
6576                         // combos' with local stores can 
6577                         // be queried via setValue()
6578                         // to set their value..
6579                         (field.store && !field.store.isLocal)
6580                         ) {
6581                         // it's a combo
6582                         var sd = { };
6583                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6584                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6585                         field.setFromData(sd);
6586                         
6587                     } else {
6588                         field.setValue(values[id]);
6589                     }
6590                     
6591                     
6592                     if(this.trackResetOnLoad){
6593                         field.originalValue = field.getValue();
6594                     }
6595                 }
6596             }
6597         }
6598          
6599         //Roo.each(this.childForms || [], function (f) {
6600         //    f.setValues(values);
6601         //});
6602                 
6603         return this;
6604     },
6605
6606     /**
6607      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6608      * they are returned as an array.
6609      * @param {Boolean} asString
6610      * @return {Object}
6611      */
6612     getValues : function(asString){
6613         //if (this.childForms) {
6614             // copy values from the child forms
6615         //    Roo.each(this.childForms, function (f) {
6616         //        this.setValues(f.getValues());
6617         //    }, this);
6618         //}
6619         
6620         
6621         
6622         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6623         if(asString === true){
6624             return fs;
6625         }
6626         return Roo.urlDecode(fs);
6627     },
6628     
6629     /**
6630      * Returns the fields in this form as an object with key/value pairs. 
6631      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6632      * @return {Object}
6633      */
6634     getFieldValues : function(with_hidden)
6635     {
6636         var items = this.getItems();
6637         var ret = {};
6638         items.each(function(f){
6639             if (!f.getName()) {
6640                 return;
6641             }
6642             var v = f.getValue();
6643             if (f.inputType =='radio') {
6644                 if (typeof(ret[f.getName()]) == 'undefined') {
6645                     ret[f.getName()] = ''; // empty..
6646                 }
6647                 
6648                 if (!f.el.dom.checked) {
6649                     return;
6650                     
6651                 }
6652                 v = f.el.dom.value;
6653                 
6654             }
6655             
6656             // not sure if this supported any more..
6657             if ((typeof(v) == 'object') && f.getRawValue) {
6658                 v = f.getRawValue() ; // dates..
6659             }
6660             // combo boxes where name != hiddenName...
6661             if (f.name != f.getName()) {
6662                 ret[f.name] = f.getRawValue();
6663             }
6664             ret[f.getName()] = v;
6665         });
6666         
6667         return ret;
6668     },
6669
6670     /**
6671      * Clears all invalid messages in this form.
6672      * @return {BasicForm} this
6673      */
6674     clearInvalid : function(){
6675         var items = this.getItems();
6676         
6677         items.each(function(f){
6678            f.clearInvalid();
6679         });
6680         
6681         
6682         
6683         return this;
6684     },
6685
6686     /**
6687      * Resets this form.
6688      * @return {BasicForm} this
6689      */
6690     reset : function(){
6691         var items = this.getItems();
6692         items.each(function(f){
6693             f.reset();
6694         });
6695         
6696         Roo.each(this.childForms || [], function (f) {
6697             f.reset();
6698         });
6699        
6700         
6701         return this;
6702     },
6703     getItems : function()
6704     {
6705         var r=new Roo.util.MixedCollection(false, function(o){
6706             return o.id || (o.id = Roo.id());
6707         });
6708         var iter = function(el) {
6709             if (el.inputEl) {
6710                 r.add(el);
6711             }
6712             if (!el.items) {
6713                 return;
6714             }
6715             Roo.each(el.items,function(e) {
6716                 iter(e);
6717             });
6718             
6719             
6720         };
6721         iter(this);
6722         return r;
6723         
6724         
6725         
6726         
6727     }
6728     
6729 });
6730
6731  
6732 /*
6733  * Based on:
6734  * Ext JS Library 1.1.1
6735  * Copyright(c) 2006-2007, Ext JS, LLC.
6736  *
6737  * Originally Released Under LGPL - original licence link has changed is not relivant.
6738  *
6739  * Fork - LGPL
6740  * <script type="text/javascript">
6741  */
6742 /**
6743  * @class Roo.form.VTypes
6744  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6745  * @singleton
6746  */
6747 Roo.form.VTypes = function(){
6748     // closure these in so they are only created once.
6749     var alpha = /^[a-zA-Z_]+$/;
6750     var alphanum = /^[a-zA-Z0-9_]+$/;
6751     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6752     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6753
6754     // All these messages and functions are configurable
6755     return {
6756         /**
6757          * The function used to validate email addresses
6758          * @param {String} value The email address
6759          */
6760         'email' : function(v){
6761             return email.test(v);
6762         },
6763         /**
6764          * The error text to display when the email validation function returns false
6765          * @type String
6766          */
6767         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6768         /**
6769          * The keystroke filter mask to be applied on email input
6770          * @type RegExp
6771          */
6772         'emailMask' : /[a-z0-9_\.\-@]/i,
6773
6774         /**
6775          * The function used to validate URLs
6776          * @param {String} value The URL
6777          */
6778         'url' : function(v){
6779             return url.test(v);
6780         },
6781         /**
6782          * The error text to display when the url validation function returns false
6783          * @type String
6784          */
6785         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6786         
6787         /**
6788          * The function used to validate alpha values
6789          * @param {String} value The value
6790          */
6791         'alpha' : function(v){
6792             return alpha.test(v);
6793         },
6794         /**
6795          * The error text to display when the alpha validation function returns false
6796          * @type String
6797          */
6798         'alphaText' : 'This field should only contain letters and _',
6799         /**
6800          * The keystroke filter mask to be applied on alpha input
6801          * @type RegExp
6802          */
6803         'alphaMask' : /[a-z_]/i,
6804
6805         /**
6806          * The function used to validate alphanumeric values
6807          * @param {String} value The value
6808          */
6809         'alphanum' : function(v){
6810             return alphanum.test(v);
6811         },
6812         /**
6813          * The error text to display when the alphanumeric validation function returns false
6814          * @type String
6815          */
6816         'alphanumText' : 'This field should only contain letters, numbers and _',
6817         /**
6818          * The keystroke filter mask to be applied on alphanumeric input
6819          * @type RegExp
6820          */
6821         'alphanumMask' : /[a-z0-9_]/i
6822     };
6823 }();/*
6824  * - LGPL
6825  *
6826  * Input
6827  * 
6828  */
6829
6830 /**
6831  * @class Roo.bootstrap.Input
6832  * @extends Roo.bootstrap.Component
6833  * Bootstrap Input class
6834  * @cfg {Boolean} disabled is it disabled
6835  * @cfg {String} fieldLabel - the label associated
6836  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6837  * @cfg {String} name name of the input
6838  * @cfg {string} fieldLabel - the label associated
6839  * @cfg {string}  inputType - input / file submit ...
6840  * @cfg {string} placeholder - placeholder to put in text.
6841  * @cfg {string}  before - input group add on before
6842  * @cfg {string} after - input group add on after
6843  * @cfg {string} size - (lg|sm) or leave empty..
6844  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6845  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6846  * @cfg {Number} md colspan out of 12 for computer-sized screens
6847  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6848  * @cfg {string} value default value of the input
6849  * @cfg {Number} labelWidth set the width of label (0-12)
6850  * @cfg {String} labelAlign (top|left)
6851  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6852  * @cfg {String} align (left|center|right) Default left
6853  * 
6854  * 
6855  * @constructor
6856  * Create a new Input
6857  * @param {Object} config The config object
6858  */
6859
6860 Roo.bootstrap.Input = function(config){
6861     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6862    
6863         this.addEvents({
6864             /**
6865              * @event focus
6866              * Fires when this field receives input focus.
6867              * @param {Roo.form.Field} this
6868              */
6869             focus : true,
6870             /**
6871              * @event blur
6872              * Fires when this field loses input focus.
6873              * @param {Roo.form.Field} this
6874              */
6875             blur : true,
6876             /**
6877              * @event specialkey
6878              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6879              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6880              * @param {Roo.form.Field} this
6881              * @param {Roo.EventObject} e The event object
6882              */
6883             specialkey : true,
6884             /**
6885              * @event change
6886              * Fires just before the field blurs if the field value has changed.
6887              * @param {Roo.form.Field} this
6888              * @param {Mixed} newValue The new value
6889              * @param {Mixed} oldValue The original value
6890              */
6891             change : true,
6892             /**
6893              * @event invalid
6894              * Fires after the field has been marked as invalid.
6895              * @param {Roo.form.Field} this
6896              * @param {String} msg The validation message
6897              */
6898             invalid : true,
6899             /**
6900              * @event valid
6901              * Fires after the field has been validated with no errors.
6902              * @param {Roo.form.Field} this
6903              */
6904             valid : true,
6905              /**
6906              * @event keyup
6907              * Fires after the key up
6908              * @param {Roo.form.Field} this
6909              * @param {Roo.EventObject}  e The event Object
6910              */
6911             keyup : true
6912         });
6913 };
6914
6915 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6916      /**
6917      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6918       automatic validation (defaults to "keyup").
6919      */
6920     validationEvent : "keyup",
6921      /**
6922      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6923      */
6924     validateOnBlur : true,
6925     /**
6926      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6927      */
6928     validationDelay : 250,
6929      /**
6930      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6931      */
6932     focusClass : "x-form-focus",  // not needed???
6933     
6934        
6935     /**
6936      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6937      */
6938     invalidClass : "has-error",
6939     
6940     /**
6941      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6942      */
6943     selectOnFocus : false,
6944     
6945      /**
6946      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6947      */
6948     maskRe : null,
6949        /**
6950      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6951      */
6952     vtype : null,
6953     
6954       /**
6955      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6956      */
6957     disableKeyFilter : false,
6958     
6959        /**
6960      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6961      */
6962     disabled : false,
6963      /**
6964      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6965      */
6966     allowBlank : true,
6967     /**
6968      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6969      */
6970     blankText : "This field is required",
6971     
6972      /**
6973      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6974      */
6975     minLength : 0,
6976     /**
6977      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6978      */
6979     maxLength : Number.MAX_VALUE,
6980     /**
6981      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6982      */
6983     minLengthText : "The minimum length for this field is {0}",
6984     /**
6985      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6986      */
6987     maxLengthText : "The maximum length for this field is {0}",
6988   
6989     
6990     /**
6991      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6992      * If available, this function will be called only after the basic validators all return true, and will be passed the
6993      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6994      */
6995     validator : null,
6996     /**
6997      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6998      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6999      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7000      */
7001     regex : null,
7002     /**
7003      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7004      */
7005     regexText : "",
7006     
7007     
7008     
7009     fieldLabel : '',
7010     inputType : 'text',
7011     
7012     name : false,
7013     placeholder: false,
7014     before : false,
7015     after : false,
7016     size : false,
7017     // private
7018     hasFocus : false,
7019     preventMark: false,
7020     isFormField : true,
7021     value : '',
7022     labelWidth : 2,
7023     labelAlign : false,
7024     readOnly : false,
7025     align : false,
7026     formatedValue : false,
7027     
7028     parentLabelAlign : function()
7029     {
7030         var parent = this;
7031         while (parent.parent()) {
7032             parent = parent.parent();
7033             if (typeof(parent.labelAlign) !='undefined') {
7034                 return parent.labelAlign;
7035             }
7036         }
7037         return 'left';
7038         
7039     },
7040     
7041     getAutoCreate : function(){
7042         
7043         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7044         
7045         var id = Roo.id();
7046         
7047         var cfg = {};
7048         
7049         if(this.inputType != 'hidden'){
7050             cfg.cls = 'form-group' //input-group
7051         }
7052         
7053         var input =  {
7054             tag: 'input',
7055             id : id,
7056             type : this.inputType,
7057             value : this.value,
7058             cls : 'form-control',
7059             placeholder : this.placeholder || ''
7060             
7061         };
7062         
7063         if(this.align){
7064             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7065         }
7066         
7067         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7068             input.maxLength = this.maxLength;
7069         }
7070         
7071         if (this.disabled) {
7072             input.disabled=true;
7073         }
7074         
7075         if (this.readOnly) {
7076             input.readonly=true;
7077         }
7078         
7079         if (this.name) {
7080             input.name = this.name;
7081         }
7082         if (this.size) {
7083             input.cls += ' input-' + this.size;
7084         }
7085         var settings=this;
7086         ['xs','sm','md','lg'].map(function(size){
7087             if (settings[size]) {
7088                 cfg.cls += ' col-' + size + '-' + settings[size];
7089             }
7090         });
7091         
7092         var inputblock = input;
7093         
7094         if (this.before || this.after) {
7095             
7096             inputblock = {
7097                 cls : 'input-group',
7098                 cn :  [] 
7099             };
7100             if (this.before && typeof(this.before) == 'string') {
7101                 
7102                 inputblock.cn.push({
7103                     tag :'span',
7104                     cls : 'roo-input-before input-group-addon',
7105                     html : this.before
7106                 });
7107             }
7108             if (this.before && typeof(this.before) == 'object') {
7109                 this.before = Roo.factory(this.before);
7110                 Roo.log(this.before);
7111                 inputblock.cn.push({
7112                     tag :'span',
7113                     cls : 'roo-input-before input-group-' +
7114                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7115                 });
7116             }
7117             
7118             inputblock.cn.push(input);
7119             
7120             if (this.after && typeof(this.after) == 'string') {
7121                 inputblock.cn.push({
7122                     tag :'span',
7123                     cls : 'roo-input-after input-group-addon',
7124                     html : this.after
7125                 });
7126             }
7127             if (this.after && typeof(this.after) == 'object') {
7128                 this.after = Roo.factory(this.after);
7129                 Roo.log(this.after);
7130                 inputblock.cn.push({
7131                     tag :'span',
7132                     cls : 'roo-input-after input-group-' +
7133                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7134                 });
7135             }
7136         };
7137         
7138         if (align ==='left' && this.fieldLabel.length) {
7139                 Roo.log("left and has label");
7140                 cfg.cn = [
7141                     
7142                     {
7143                         tag: 'label',
7144                         'for' :  id,
7145                         cls : 'control-label col-sm-' + this.labelWidth,
7146                         html : this.fieldLabel
7147                         
7148                     },
7149                     {
7150                         cls : "col-sm-" + (12 - this.labelWidth), 
7151                         cn: [
7152                             inputblock
7153                         ]
7154                     }
7155                     
7156                 ];
7157         } else if ( this.fieldLabel.length) {
7158                 Roo.log(" label");
7159                  cfg.cn = [
7160                    
7161                     {
7162                         tag: 'label',
7163                         //cls : 'input-group-addon',
7164                         html : this.fieldLabel
7165                         
7166                     },
7167                     
7168                     inputblock
7169                     
7170                 ];
7171
7172         } else {
7173             
7174                 Roo.log(" no label && no align");
7175                 cfg.cn = [
7176                     
7177                         inputblock
7178                     
7179                 ];
7180                 
7181                 
7182         };
7183         Roo.log('input-parentType: ' + this.parentType);
7184         
7185         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7186            cfg.cls += ' navbar-form';
7187            Roo.log(cfg);
7188         }
7189         
7190         return cfg;
7191         
7192     },
7193     /**
7194      * return the real input element.
7195      */
7196     inputEl: function ()
7197     {
7198         return this.el.select('input.form-control',true).first();
7199     },
7200     setDisabled : function(v)
7201     {
7202         var i  = this.inputEl().dom;
7203         if (!v) {
7204             i.removeAttribute('disabled');
7205             return;
7206             
7207         }
7208         i.setAttribute('disabled','true');
7209     },
7210     initEvents : function()
7211     {
7212         
7213         this.inputEl().on("keydown" , this.fireKey,  this);
7214         this.inputEl().on("focus", this.onFocus,  this);
7215         this.inputEl().on("blur", this.onBlur,  this);
7216         
7217         this.inputEl().relayEvent('keyup', this);
7218
7219         // reference to original value for reset
7220         this.originalValue = this.getValue();
7221         //Roo.form.TextField.superclass.initEvents.call(this);
7222         if(this.validationEvent == 'keyup'){
7223             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7224             this.inputEl().on('keyup', this.filterValidation, this);
7225         }
7226         else if(this.validationEvent !== false){
7227             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7228         }
7229         
7230         if(this.selectOnFocus){
7231             this.on("focus", this.preFocus, this);
7232             
7233         }
7234         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7235             this.inputEl().on("keypress", this.filterKeys, this);
7236         }
7237        /* if(this.grow){
7238             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7239             this.el.on("click", this.autoSize,  this);
7240         }
7241         */
7242         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7243             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7244         }
7245         
7246         if (typeof(this.before) == 'object') {
7247             this.before.render(this.el.select('.roo-input-before',true).first());
7248         }
7249         if (typeof(this.after) == 'object') {
7250             this.after.render(this.el.select('.roo-input-after',true).first());
7251         }
7252         
7253         
7254     },
7255     filterValidation : function(e){
7256         if(!e.isNavKeyPress()){
7257             this.validationTask.delay(this.validationDelay);
7258         }
7259     },
7260      /**
7261      * Validates the field value
7262      * @return {Boolean} True if the value is valid, else false
7263      */
7264     validate : function(){
7265         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7266         if(this.disabled || this.validateValue(this.getRawValue())){
7267             this.clearInvalid();
7268             return true;
7269         }
7270         return false;
7271     },
7272     
7273     
7274     /**
7275      * Validates a value according to the field's validation rules and marks the field as invalid
7276      * if the validation fails
7277      * @param {Mixed} value The value to validate
7278      * @return {Boolean} True if the value is valid, else false
7279      */
7280     validateValue : function(value){
7281         if(value.length < 1)  { // if it's blank
7282              if(this.allowBlank){
7283                 this.clearInvalid();
7284                 return true;
7285              }else{
7286                 this.markInvalid(this.blankText);
7287                 return false;
7288              }
7289         }
7290         if(value.length < this.minLength){
7291             this.markInvalid(String.format(this.minLengthText, this.minLength));
7292             return false;
7293         }
7294         if(value.length > this.maxLength){
7295             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7296             return false;
7297         }
7298         if(this.vtype){
7299             var vt = Roo.form.VTypes;
7300             if(!vt[this.vtype](value, this)){
7301                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7302                 return false;
7303             }
7304         }
7305         if(typeof this.validator == "function"){
7306             var msg = this.validator(value);
7307             if(msg !== true){
7308                 this.markInvalid(msg);
7309                 return false;
7310             }
7311         }
7312         if(this.regex && !this.regex.test(value)){
7313             this.markInvalid(this.regexText);
7314             return false;
7315         }
7316         return true;
7317     },
7318
7319     
7320     
7321      // private
7322     fireKey : function(e){
7323         //Roo.log('field ' + e.getKey());
7324         if(e.isNavKeyPress()){
7325             this.fireEvent("specialkey", this, e);
7326         }
7327     },
7328     focus : function (selectText){
7329         if(this.rendered){
7330             this.inputEl().focus();
7331             if(selectText === true){
7332                 this.inputEl().dom.select();
7333             }
7334         }
7335         return this;
7336     } ,
7337     
7338     onFocus : function(){
7339         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7340            // this.el.addClass(this.focusClass);
7341         }
7342         if(!this.hasFocus){
7343             this.hasFocus = true;
7344             this.startValue = this.getValue();
7345             this.fireEvent("focus", this);
7346         }
7347     },
7348     
7349     beforeBlur : Roo.emptyFn,
7350
7351     
7352     // private
7353     onBlur : function(){
7354         this.beforeBlur();
7355         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7356             //this.el.removeClass(this.focusClass);
7357         }
7358         this.hasFocus = false;
7359         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7360             this.validate();
7361         }
7362         var v = this.getValue();
7363         if(String(v) !== String(this.startValue)){
7364             this.fireEvent('change', this, v, this.startValue);
7365         }
7366         this.fireEvent("blur", this);
7367     },
7368     
7369     /**
7370      * Resets the current field value to the originally loaded value and clears any validation messages
7371      */
7372     reset : function(){
7373         this.setValue(this.originalValue);
7374         this.clearInvalid();
7375     },
7376      /**
7377      * Returns the name of the field
7378      * @return {Mixed} name The name field
7379      */
7380     getName: function(){
7381         return this.name;
7382     },
7383      /**
7384      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7385      * @return {Mixed} value The field value
7386      */
7387     getValue : function(){
7388         
7389         var v = this.inputEl().getValue();
7390         
7391         return v;
7392     },
7393     /**
7394      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7395      * @return {Mixed} value The field value
7396      */
7397     getRawValue : function(){
7398         var v = this.inputEl().getValue();
7399         
7400         return v;
7401     },
7402     
7403     /**
7404      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7405      * @param {Mixed} value The value to set
7406      */
7407     setRawValue : function(v){
7408         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7409     },
7410     
7411     selectText : function(start, end){
7412         var v = this.getRawValue();
7413         if(v.length > 0){
7414             start = start === undefined ? 0 : start;
7415             end = end === undefined ? v.length : end;
7416             var d = this.inputEl().dom;
7417             if(d.setSelectionRange){
7418                 d.setSelectionRange(start, end);
7419             }else if(d.createTextRange){
7420                 var range = d.createTextRange();
7421                 range.moveStart("character", start);
7422                 range.moveEnd("character", v.length-end);
7423                 range.select();
7424             }
7425         }
7426     },
7427     
7428     /**
7429      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7430      * @param {Mixed} value The value to set
7431      */
7432     setValue : function(v){
7433         this.value = v;
7434         if(this.rendered){
7435             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7436             this.validate();
7437         }
7438     },
7439     
7440     /*
7441     processValue : function(value){
7442         if(this.stripCharsRe){
7443             var newValue = value.replace(this.stripCharsRe, '');
7444             if(newValue !== value){
7445                 this.setRawValue(newValue);
7446                 return newValue;
7447             }
7448         }
7449         return value;
7450     },
7451   */
7452     preFocus : function(){
7453         
7454         if(this.selectOnFocus){
7455             this.inputEl().dom.select();
7456         }
7457     },
7458     filterKeys : function(e){
7459         var k = e.getKey();
7460         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7461             return;
7462         }
7463         var c = e.getCharCode(), cc = String.fromCharCode(c);
7464         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7465             return;
7466         }
7467         if(!this.maskRe.test(cc)){
7468             e.stopEvent();
7469         }
7470     },
7471      /**
7472      * Clear any invalid styles/messages for this field
7473      */
7474     clearInvalid : function(){
7475         
7476         if(!this.el || this.preventMark){ // not rendered
7477             return;
7478         }
7479         this.el.removeClass(this.invalidClass);
7480         /*
7481         switch(this.msgTarget){
7482             case 'qtip':
7483                 this.el.dom.qtip = '';
7484                 break;
7485             case 'title':
7486                 this.el.dom.title = '';
7487                 break;
7488             case 'under':
7489                 if(this.errorEl){
7490                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7491                 }
7492                 break;
7493             case 'side':
7494                 if(this.errorIcon){
7495                     this.errorIcon.dom.qtip = '';
7496                     this.errorIcon.hide();
7497                     this.un('resize', this.alignErrorIcon, this);
7498                 }
7499                 break;
7500             default:
7501                 var t = Roo.getDom(this.msgTarget);
7502                 t.innerHTML = '';
7503                 t.style.display = 'none';
7504                 break;
7505         }
7506         */
7507         this.fireEvent('valid', this);
7508     },
7509      /**
7510      * Mark this field as invalid
7511      * @param {String} msg The validation message
7512      */
7513     markInvalid : function(msg){
7514         if(!this.el  || this.preventMark){ // not rendered
7515             return;
7516         }
7517         this.el.addClass(this.invalidClass);
7518         /*
7519         msg = msg || this.invalidText;
7520         switch(this.msgTarget){
7521             case 'qtip':
7522                 this.el.dom.qtip = msg;
7523                 this.el.dom.qclass = 'x-form-invalid-tip';
7524                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7525                     Roo.QuickTips.enable();
7526                 }
7527                 break;
7528             case 'title':
7529                 this.el.dom.title = msg;
7530                 break;
7531             case 'under':
7532                 if(!this.errorEl){
7533                     var elp = this.el.findParent('.x-form-element', 5, true);
7534                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7535                     this.errorEl.setWidth(elp.getWidth(true)-20);
7536                 }
7537                 this.errorEl.update(msg);
7538                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7539                 break;
7540             case 'side':
7541                 if(!this.errorIcon){
7542                     var elp = this.el.findParent('.x-form-element', 5, true);
7543                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7544                 }
7545                 this.alignErrorIcon();
7546                 this.errorIcon.dom.qtip = msg;
7547                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7548                 this.errorIcon.show();
7549                 this.on('resize', this.alignErrorIcon, this);
7550                 break;
7551             default:
7552                 var t = Roo.getDom(this.msgTarget);
7553                 t.innerHTML = msg;
7554                 t.style.display = this.msgDisplay;
7555                 break;
7556         }
7557         */
7558         this.fireEvent('invalid', this, msg);
7559     },
7560     // private
7561     SafariOnKeyDown : function(event)
7562     {
7563         // this is a workaround for a password hang bug on chrome/ webkit.
7564         
7565         var isSelectAll = false;
7566         
7567         if(this.inputEl().dom.selectionEnd > 0){
7568             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7569         }
7570         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7571             event.preventDefault();
7572             this.setValue('');
7573             return;
7574         }
7575         
7576         if(isSelectAll){ // backspace and delete key
7577             
7578             event.preventDefault();
7579             // this is very hacky as keydown always get's upper case.
7580             //
7581             var cc = String.fromCharCode(event.getCharCode());
7582             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7583             
7584         }
7585     },
7586     adjustWidth : function(tag, w){
7587         tag = tag.toLowerCase();
7588         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7589             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7590                 if(tag == 'input'){
7591                     return w + 2;
7592                 }
7593                 if(tag == 'textarea'){
7594                     return w-2;
7595                 }
7596             }else if(Roo.isOpera){
7597                 if(tag == 'input'){
7598                     return w + 2;
7599                 }
7600                 if(tag == 'textarea'){
7601                     return w-2;
7602                 }
7603             }
7604         }
7605         return w;
7606     }
7607     
7608 });
7609
7610  
7611 /*
7612  * - LGPL
7613  *
7614  * Input
7615  * 
7616  */
7617
7618 /**
7619  * @class Roo.bootstrap.TextArea
7620  * @extends Roo.bootstrap.Input
7621  * Bootstrap TextArea class
7622  * @cfg {Number} cols Specifies the visible width of a text area
7623  * @cfg {Number} rows Specifies the visible number of lines in a text area
7624  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7625  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7626  * @cfg {string} html text
7627  * 
7628  * @constructor
7629  * Create a new TextArea
7630  * @param {Object} config The config object
7631  */
7632
7633 Roo.bootstrap.TextArea = function(config){
7634     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7635    
7636 };
7637
7638 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7639      
7640     cols : false,
7641     rows : 5,
7642     readOnly : false,
7643     warp : 'soft',
7644     resize : false,
7645     value: false,
7646     html: false,
7647     
7648     getAutoCreate : function(){
7649         
7650         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7651         
7652         var id = Roo.id();
7653         
7654         var cfg = {};
7655         
7656         var input =  {
7657             tag: 'textarea',
7658             id : id,
7659             warp : this.warp,
7660             rows : this.rows,
7661             value : this.value || '',
7662             html: this.html || '',
7663             cls : 'form-control',
7664             placeholder : this.placeholder || '' 
7665             
7666         };
7667         
7668         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7669             input.maxLength = this.maxLength;
7670         }
7671         
7672         if(this.resize){
7673             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7674         }
7675         
7676         if(this.cols){
7677             input.cols = this.cols;
7678         }
7679         
7680         if (this.readOnly) {
7681             input.readonly = true;
7682         }
7683         
7684         if (this.name) {
7685             input.name = this.name;
7686         }
7687         
7688         if (this.size) {
7689             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7690         }
7691         
7692         var settings=this;
7693         ['xs','sm','md','lg'].map(function(size){
7694             if (settings[size]) {
7695                 cfg.cls += ' col-' + size + '-' + settings[size];
7696             }
7697         });
7698         
7699         var inputblock = input;
7700         
7701         if (this.before || this.after) {
7702             
7703             inputblock = {
7704                 cls : 'input-group',
7705                 cn :  [] 
7706             };
7707             if (this.before) {
7708                 inputblock.cn.push({
7709                     tag :'span',
7710                     cls : 'input-group-addon',
7711                     html : this.before
7712                 });
7713             }
7714             inputblock.cn.push(input);
7715             if (this.after) {
7716                 inputblock.cn.push({
7717                     tag :'span',
7718                     cls : 'input-group-addon',
7719                     html : this.after
7720                 });
7721             }
7722             
7723         }
7724         
7725         if (align ==='left' && this.fieldLabel.length) {
7726                 Roo.log("left and has label");
7727                 cfg.cn = [
7728                     
7729                     {
7730                         tag: 'label',
7731                         'for' :  id,
7732                         cls : 'control-label col-sm-' + this.labelWidth,
7733                         html : this.fieldLabel
7734                         
7735                     },
7736                     {
7737                         cls : "col-sm-" + (12 - this.labelWidth), 
7738                         cn: [
7739                             inputblock
7740                         ]
7741                     }
7742                     
7743                 ];
7744         } else if ( this.fieldLabel.length) {
7745                 Roo.log(" label");
7746                  cfg.cn = [
7747                    
7748                     {
7749                         tag: 'label',
7750                         //cls : 'input-group-addon',
7751                         html : this.fieldLabel
7752                         
7753                     },
7754                     
7755                     inputblock
7756                     
7757                 ];
7758
7759         } else {
7760             
7761                    Roo.log(" no label && no align");
7762                 cfg.cn = [
7763                     
7764                         inputblock
7765                     
7766                 ];
7767                 
7768                 
7769         }
7770         
7771         if (this.disabled) {
7772             input.disabled=true;
7773         }
7774         
7775         return cfg;
7776         
7777     },
7778     /**
7779      * return the real textarea element.
7780      */
7781     inputEl: function ()
7782     {
7783         return this.el.select('textarea.form-control',true).first();
7784     }
7785 });
7786
7787  
7788 /*
7789  * - LGPL
7790  *
7791  * trigger field - base class for combo..
7792  * 
7793  */
7794  
7795 /**
7796  * @class Roo.bootstrap.TriggerField
7797  * @extends Roo.bootstrap.Input
7798  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7799  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7800  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7801  * for which you can provide a custom implementation.  For example:
7802  * <pre><code>
7803 var trigger = new Roo.bootstrap.TriggerField();
7804 trigger.onTriggerClick = myTriggerFn;
7805 trigger.applyTo('my-field');
7806 </code></pre>
7807  *
7808  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7809  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7810  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7811  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7812  * @constructor
7813  * Create a new TriggerField.
7814  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7815  * to the base TextField)
7816  */
7817 Roo.bootstrap.TriggerField = function(config){
7818     this.mimicing = false;
7819     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7820 };
7821
7822 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7823     /**
7824      * @cfg {String} triggerClass A CSS class to apply to the trigger
7825      */
7826      /**
7827      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7828      */
7829     hideTrigger:false,
7830
7831     /** @cfg {Boolean} grow @hide */
7832     /** @cfg {Number} growMin @hide */
7833     /** @cfg {Number} growMax @hide */
7834
7835     /**
7836      * @hide 
7837      * @method
7838      */
7839     autoSize: Roo.emptyFn,
7840     // private
7841     monitorTab : true,
7842     // private
7843     deferHeight : true,
7844
7845     
7846     actionMode : 'wrap',
7847     
7848     
7849     
7850     getAutoCreate : function(){
7851        
7852         var align = this.labelAlign || this.parentLabelAlign();
7853         
7854         var id = Roo.id();
7855         
7856         var cfg = {
7857             cls: 'form-group' //input-group
7858         };
7859         
7860         
7861         var input =  {
7862             tag: 'input',
7863             id : id,
7864             type : this.inputType,
7865             cls : 'form-control',
7866             autocomplete: 'off',
7867             placeholder : this.placeholder || '' 
7868             
7869         };
7870         if (this.name) {
7871             input.name = this.name;
7872         }
7873         if (this.size) {
7874             input.cls += ' input-' + this.size;
7875         }
7876         
7877         if (this.disabled) {
7878             input.disabled=true;
7879         }
7880         
7881         var inputblock = input;
7882         
7883         if (this.before || this.after) {
7884             
7885             inputblock = {
7886                 cls : 'input-group',
7887                 cn :  [] 
7888             };
7889             if (this.before) {
7890                 inputblock.cn.push({
7891                     tag :'span',
7892                     cls : 'input-group-addon',
7893                     html : this.before
7894                 });
7895             }
7896             inputblock.cn.push(input);
7897             if (this.after) {
7898                 inputblock.cn.push({
7899                     tag :'span',
7900                     cls : 'input-group-addon',
7901                     html : this.after
7902                 });
7903             }
7904             
7905         };
7906         
7907         var box = {
7908             tag: 'div',
7909             cn: [
7910                 {
7911                     tag: 'input',
7912                     type : 'hidden',
7913                     cls: 'form-hidden-field'
7914                 },
7915                 inputblock
7916             ]
7917             
7918         };
7919         
7920         if(this.multiple){
7921             Roo.log('multiple');
7922             
7923             box = {
7924                 tag: 'div',
7925                 cn: [
7926                     {
7927                         tag: 'input',
7928                         type : 'hidden',
7929                         cls: 'form-hidden-field'
7930                     },
7931                     {
7932                         tag: 'ul',
7933                         cls: 'select2-choices',
7934                         cn:[
7935                             {
7936                                 tag: 'li',
7937                                 cls: 'select2-search-field',
7938                                 cn: [
7939
7940                                     inputblock
7941                                 ]
7942                             }
7943                         ]
7944                     }
7945                 ]
7946             }
7947         };
7948         
7949         var combobox = {
7950             cls: 'select2-container input-group',
7951             cn: [
7952                 box
7953 //                {
7954 //                    tag: 'ul',
7955 //                    cls: 'typeahead typeahead-long dropdown-menu',
7956 //                    style: 'display:none'
7957 //                }
7958             ]
7959         };
7960         
7961         if(!this.multiple && this.showToggleBtn){
7962             combobox.cn.push({
7963                 tag :'span',
7964                 cls : 'input-group-addon btn dropdown-toggle',
7965                 cn : [
7966                     {
7967                         tag: 'span',
7968                         cls: 'caret'
7969                     },
7970                     {
7971                         tag: 'span',
7972                         cls: 'combobox-clear',
7973                         cn  : [
7974                             {
7975                                 tag : 'i',
7976                                 cls: 'icon-remove'
7977                             }
7978                         ]
7979                     }
7980                 ]
7981
7982             })
7983         }
7984         
7985         if(this.multiple){
7986             combobox.cls += ' select2-container-multi';
7987         }
7988         
7989         if (align ==='left' && this.fieldLabel.length) {
7990             
7991                 Roo.log("left and has label");
7992                 cfg.cn = [
7993                     
7994                     {
7995                         tag: 'label',
7996                         'for' :  id,
7997                         cls : 'control-label col-sm-' + this.labelWidth,
7998                         html : this.fieldLabel
7999                         
8000                     },
8001                     {
8002                         cls : "col-sm-" + (12 - this.labelWidth), 
8003                         cn: [
8004                             combobox
8005                         ]
8006                     }
8007                     
8008                 ];
8009         } else if ( this.fieldLabel.length) {
8010                 Roo.log(" label");
8011                  cfg.cn = [
8012                    
8013                     {
8014                         tag: 'label',
8015                         //cls : 'input-group-addon',
8016                         html : this.fieldLabel
8017                         
8018                     },
8019                     
8020                     combobox
8021                     
8022                 ];
8023
8024         } else {
8025             
8026                 Roo.log(" no label && no align");
8027                 cfg = combobox
8028                      
8029                 
8030         }
8031          
8032         var settings=this;
8033         ['xs','sm','md','lg'].map(function(size){
8034             if (settings[size]) {
8035                 cfg.cls += ' col-' + size + '-' + settings[size];
8036             }
8037         });
8038         
8039         return cfg;
8040         
8041     },
8042     
8043     
8044     
8045     // private
8046     onResize : function(w, h){
8047 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8048 //        if(typeof w == 'number'){
8049 //            var x = w - this.trigger.getWidth();
8050 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8051 //            this.trigger.setStyle('left', x+'px');
8052 //        }
8053     },
8054
8055     // private
8056     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8057
8058     // private
8059     getResizeEl : function(){
8060         return this.inputEl();
8061     },
8062
8063     // private
8064     getPositionEl : function(){
8065         return this.inputEl();
8066     },
8067
8068     // private
8069     alignErrorIcon : function(){
8070         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8071     },
8072
8073     // private
8074     initEvents : function(){
8075         
8076         this.createList();
8077         
8078         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8079         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8080         if(!this.multiple && this.showToggleBtn){
8081             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8082             if(this.hideTrigger){
8083                 this.trigger.setDisplayed(false);
8084             }
8085             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8086         }
8087         
8088         if(this.multiple){
8089             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8090         }
8091         
8092         //this.trigger.addClassOnOver('x-form-trigger-over');
8093         //this.trigger.addClassOnClick('x-form-trigger-click');
8094         
8095         //if(!this.width){
8096         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8097         //}
8098     },
8099     
8100     createList : function()
8101     {
8102         this.list = Roo.get(document.body).createChild({
8103             tag: 'ul',
8104             cls: 'typeahead typeahead-long dropdown-menu',
8105             style: 'display:none'
8106         });
8107         
8108         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8109         
8110     },
8111
8112     // private
8113     initTrigger : function(){
8114        
8115     },
8116
8117     // private
8118     onDestroy : function(){
8119         if(this.trigger){
8120             this.trigger.removeAllListeners();
8121           //  this.trigger.remove();
8122         }
8123         //if(this.wrap){
8124         //    this.wrap.remove();
8125         //}
8126         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8127     },
8128
8129     // private
8130     onFocus : function(){
8131         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8132         /*
8133         if(!this.mimicing){
8134             this.wrap.addClass('x-trigger-wrap-focus');
8135             this.mimicing = true;
8136             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8137             if(this.monitorTab){
8138                 this.el.on("keydown", this.checkTab, this);
8139             }
8140         }
8141         */
8142     },
8143
8144     // private
8145     checkTab : function(e){
8146         if(e.getKey() == e.TAB){
8147             this.triggerBlur();
8148         }
8149     },
8150
8151     // private
8152     onBlur : function(){
8153         // do nothing
8154     },
8155
8156     // private
8157     mimicBlur : function(e, t){
8158         /*
8159         if(!this.wrap.contains(t) && this.validateBlur()){
8160             this.triggerBlur();
8161         }
8162         */
8163     },
8164
8165     // private
8166     triggerBlur : function(){
8167         this.mimicing = false;
8168         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8169         if(this.monitorTab){
8170             this.el.un("keydown", this.checkTab, this);
8171         }
8172         //this.wrap.removeClass('x-trigger-wrap-focus');
8173         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8174     },
8175
8176     // private
8177     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8178     validateBlur : function(e, t){
8179         return true;
8180     },
8181
8182     // private
8183     onDisable : function(){
8184         this.inputEl().dom.disabled = true;
8185         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8186         //if(this.wrap){
8187         //    this.wrap.addClass('x-item-disabled');
8188         //}
8189     },
8190
8191     // private
8192     onEnable : function(){
8193         this.inputEl().dom.disabled = false;
8194         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8195         //if(this.wrap){
8196         //    this.el.removeClass('x-item-disabled');
8197         //}
8198     },
8199
8200     // private
8201     onShow : function(){
8202         var ae = this.getActionEl();
8203         
8204         if(ae){
8205             ae.dom.style.display = '';
8206             ae.dom.style.visibility = 'visible';
8207         }
8208     },
8209
8210     // private
8211     
8212     onHide : function(){
8213         var ae = this.getActionEl();
8214         ae.dom.style.display = 'none';
8215     },
8216
8217     /**
8218      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8219      * by an implementing function.
8220      * @method
8221      * @param {EventObject} e
8222      */
8223     onTriggerClick : Roo.emptyFn
8224 });
8225  /*
8226  * Based on:
8227  * Ext JS Library 1.1.1
8228  * Copyright(c) 2006-2007, Ext JS, LLC.
8229  *
8230  * Originally Released Under LGPL - original licence link has changed is not relivant.
8231  *
8232  * Fork - LGPL
8233  * <script type="text/javascript">
8234  */
8235
8236
8237 /**
8238  * @class Roo.data.SortTypes
8239  * @singleton
8240  * Defines the default sorting (casting?) comparison functions used when sorting data.
8241  */
8242 Roo.data.SortTypes = {
8243     /**
8244      * Default sort that does nothing
8245      * @param {Mixed} s The value being converted
8246      * @return {Mixed} The comparison value
8247      */
8248     none : function(s){
8249         return s;
8250     },
8251     
8252     /**
8253      * The regular expression used to strip tags
8254      * @type {RegExp}
8255      * @property
8256      */
8257     stripTagsRE : /<\/?[^>]+>/gi,
8258     
8259     /**
8260      * Strips all HTML tags to sort on text only
8261      * @param {Mixed} s The value being converted
8262      * @return {String} The comparison value
8263      */
8264     asText : function(s){
8265         return String(s).replace(this.stripTagsRE, "");
8266     },
8267     
8268     /**
8269      * Strips all HTML tags to sort on text only - Case insensitive
8270      * @param {Mixed} s The value being converted
8271      * @return {String} The comparison value
8272      */
8273     asUCText : function(s){
8274         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8275     },
8276     
8277     /**
8278      * Case insensitive string
8279      * @param {Mixed} s The value being converted
8280      * @return {String} The comparison value
8281      */
8282     asUCString : function(s) {
8283         return String(s).toUpperCase();
8284     },
8285     
8286     /**
8287      * Date sorting
8288      * @param {Mixed} s The value being converted
8289      * @return {Number} The comparison value
8290      */
8291     asDate : function(s) {
8292         if(!s){
8293             return 0;
8294         }
8295         if(s instanceof Date){
8296             return s.getTime();
8297         }
8298         return Date.parse(String(s));
8299     },
8300     
8301     /**
8302      * Float sorting
8303      * @param {Mixed} s The value being converted
8304      * @return {Float} The comparison value
8305      */
8306     asFloat : function(s) {
8307         var val = parseFloat(String(s).replace(/,/g, ""));
8308         if(isNaN(val)) val = 0;
8309         return val;
8310     },
8311     
8312     /**
8313      * Integer sorting
8314      * @param {Mixed} s The value being converted
8315      * @return {Number} The comparison value
8316      */
8317     asInt : function(s) {
8318         var val = parseInt(String(s).replace(/,/g, ""));
8319         if(isNaN(val)) val = 0;
8320         return val;
8321     }
8322 };/*
8323  * Based on:
8324  * Ext JS Library 1.1.1
8325  * Copyright(c) 2006-2007, Ext JS, LLC.
8326  *
8327  * Originally Released Under LGPL - original licence link has changed is not relivant.
8328  *
8329  * Fork - LGPL
8330  * <script type="text/javascript">
8331  */
8332
8333 /**
8334 * @class Roo.data.Record
8335  * Instances of this class encapsulate both record <em>definition</em> information, and record
8336  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8337  * to access Records cached in an {@link Roo.data.Store} object.<br>
8338  * <p>
8339  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8340  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8341  * objects.<br>
8342  * <p>
8343  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8344  * @constructor
8345  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8346  * {@link #create}. The parameters are the same.
8347  * @param {Array} data An associative Array of data values keyed by the field name.
8348  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8349  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8350  * not specified an integer id is generated.
8351  */
8352 Roo.data.Record = function(data, id){
8353     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8354     this.data = data;
8355 };
8356
8357 /**
8358  * Generate a constructor for a specific record layout.
8359  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8360  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8361  * Each field definition object may contain the following properties: <ul>
8362  * <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,
8363  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8364  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8365  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8366  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8367  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8368  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8369  * this may be omitted.</p></li>
8370  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8371  * <ul><li>auto (Default, implies no conversion)</li>
8372  * <li>string</li>
8373  * <li>int</li>
8374  * <li>float</li>
8375  * <li>boolean</li>
8376  * <li>date</li></ul></p></li>
8377  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8378  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8379  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8380  * by the Reader into an object that will be stored in the Record. It is passed the
8381  * following parameters:<ul>
8382  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8383  * </ul></p></li>
8384  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8385  * </ul>
8386  * <br>usage:<br><pre><code>
8387 var TopicRecord = Roo.data.Record.create(
8388     {name: 'title', mapping: 'topic_title'},
8389     {name: 'author', mapping: 'username'},
8390     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8391     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8392     {name: 'lastPoster', mapping: 'user2'},
8393     {name: 'excerpt', mapping: 'post_text'}
8394 );
8395
8396 var myNewRecord = new TopicRecord({
8397     title: 'Do my job please',
8398     author: 'noobie',
8399     totalPosts: 1,
8400     lastPost: new Date(),
8401     lastPoster: 'Animal',
8402     excerpt: 'No way dude!'
8403 });
8404 myStore.add(myNewRecord);
8405 </code></pre>
8406  * @method create
8407  * @static
8408  */
8409 Roo.data.Record.create = function(o){
8410     var f = function(){
8411         f.superclass.constructor.apply(this, arguments);
8412     };
8413     Roo.extend(f, Roo.data.Record);
8414     var p = f.prototype;
8415     p.fields = new Roo.util.MixedCollection(false, function(field){
8416         return field.name;
8417     });
8418     for(var i = 0, len = o.length; i < len; i++){
8419         p.fields.add(new Roo.data.Field(o[i]));
8420     }
8421     f.getField = function(name){
8422         return p.fields.get(name);  
8423     };
8424     return f;
8425 };
8426
8427 Roo.data.Record.AUTO_ID = 1000;
8428 Roo.data.Record.EDIT = 'edit';
8429 Roo.data.Record.REJECT = 'reject';
8430 Roo.data.Record.COMMIT = 'commit';
8431
8432 Roo.data.Record.prototype = {
8433     /**
8434      * Readonly flag - true if this record has been modified.
8435      * @type Boolean
8436      */
8437     dirty : false,
8438     editing : false,
8439     error: null,
8440     modified: null,
8441
8442     // private
8443     join : function(store){
8444         this.store = store;
8445     },
8446
8447     /**
8448      * Set the named field to the specified value.
8449      * @param {String} name The name of the field to set.
8450      * @param {Object} value The value to set the field to.
8451      */
8452     set : function(name, value){
8453         if(this.data[name] == value){
8454             return;
8455         }
8456         this.dirty = true;
8457         if(!this.modified){
8458             this.modified = {};
8459         }
8460         if(typeof this.modified[name] == 'undefined'){
8461             this.modified[name] = this.data[name];
8462         }
8463         this.data[name] = value;
8464         if(!this.editing && this.store){
8465             this.store.afterEdit(this);
8466         }       
8467     },
8468
8469     /**
8470      * Get the value of the named field.
8471      * @param {String} name The name of the field to get the value of.
8472      * @return {Object} The value of the field.
8473      */
8474     get : function(name){
8475         return this.data[name]; 
8476     },
8477
8478     // private
8479     beginEdit : function(){
8480         this.editing = true;
8481         this.modified = {}; 
8482     },
8483
8484     // private
8485     cancelEdit : function(){
8486         this.editing = false;
8487         delete this.modified;
8488     },
8489
8490     // private
8491     endEdit : function(){
8492         this.editing = false;
8493         if(this.dirty && this.store){
8494             this.store.afterEdit(this);
8495         }
8496     },
8497
8498     /**
8499      * Usually called by the {@link Roo.data.Store} which owns the Record.
8500      * Rejects all changes made to the Record since either creation, or the last commit operation.
8501      * Modified fields are reverted to their original values.
8502      * <p>
8503      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8504      * of reject operations.
8505      */
8506     reject : function(){
8507         var m = this.modified;
8508         for(var n in m){
8509             if(typeof m[n] != "function"){
8510                 this.data[n] = m[n];
8511             }
8512         }
8513         this.dirty = false;
8514         delete this.modified;
8515         this.editing = false;
8516         if(this.store){
8517             this.store.afterReject(this);
8518         }
8519     },
8520
8521     /**
8522      * Usually called by the {@link Roo.data.Store} which owns the Record.
8523      * Commits all changes made to the Record since either creation, or the last commit operation.
8524      * <p>
8525      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8526      * of commit operations.
8527      */
8528     commit : function(){
8529         this.dirty = false;
8530         delete this.modified;
8531         this.editing = false;
8532         if(this.store){
8533             this.store.afterCommit(this);
8534         }
8535     },
8536
8537     // private
8538     hasError : function(){
8539         return this.error != null;
8540     },
8541
8542     // private
8543     clearError : function(){
8544         this.error = null;
8545     },
8546
8547     /**
8548      * Creates a copy of this record.
8549      * @param {String} id (optional) A new record id if you don't want to use this record's id
8550      * @return {Record}
8551      */
8552     copy : function(newId) {
8553         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8554     }
8555 };/*
8556  * Based on:
8557  * Ext JS Library 1.1.1
8558  * Copyright(c) 2006-2007, Ext JS, LLC.
8559  *
8560  * Originally Released Under LGPL - original licence link has changed is not relivant.
8561  *
8562  * Fork - LGPL
8563  * <script type="text/javascript">
8564  */
8565
8566
8567
8568 /**
8569  * @class Roo.data.Store
8570  * @extends Roo.util.Observable
8571  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8572  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8573  * <p>
8574  * 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
8575  * has no knowledge of the format of the data returned by the Proxy.<br>
8576  * <p>
8577  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8578  * instances from the data object. These records are cached and made available through accessor functions.
8579  * @constructor
8580  * Creates a new Store.
8581  * @param {Object} config A config object containing the objects needed for the Store to access data,
8582  * and read the data into Records.
8583  */
8584 Roo.data.Store = function(config){
8585     this.data = new Roo.util.MixedCollection(false);
8586     this.data.getKey = function(o){
8587         return o.id;
8588     };
8589     this.baseParams = {};
8590     // private
8591     this.paramNames = {
8592         "start" : "start",
8593         "limit" : "limit",
8594         "sort" : "sort",
8595         "dir" : "dir",
8596         "multisort" : "_multisort"
8597     };
8598
8599     if(config && config.data){
8600         this.inlineData = config.data;
8601         delete config.data;
8602     }
8603
8604     Roo.apply(this, config);
8605     
8606     if(this.reader){ // reader passed
8607         this.reader = Roo.factory(this.reader, Roo.data);
8608         this.reader.xmodule = this.xmodule || false;
8609         if(!this.recordType){
8610             this.recordType = this.reader.recordType;
8611         }
8612         if(this.reader.onMetaChange){
8613             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8614         }
8615     }
8616
8617     if(this.recordType){
8618         this.fields = this.recordType.prototype.fields;
8619     }
8620     this.modified = [];
8621
8622     this.addEvents({
8623         /**
8624          * @event datachanged
8625          * Fires when the data cache has changed, and a widget which is using this Store
8626          * as a Record cache should refresh its view.
8627          * @param {Store} this
8628          */
8629         datachanged : true,
8630         /**
8631          * @event metachange
8632          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8633          * @param {Store} this
8634          * @param {Object} meta The JSON metadata
8635          */
8636         metachange : true,
8637         /**
8638          * @event add
8639          * Fires when Records have been added to the Store
8640          * @param {Store} this
8641          * @param {Roo.data.Record[]} records The array of Records added
8642          * @param {Number} index The index at which the record(s) were added
8643          */
8644         add : true,
8645         /**
8646          * @event remove
8647          * Fires when a Record has been removed from the Store
8648          * @param {Store} this
8649          * @param {Roo.data.Record} record The Record that was removed
8650          * @param {Number} index The index at which the record was removed
8651          */
8652         remove : true,
8653         /**
8654          * @event update
8655          * Fires when a Record has been updated
8656          * @param {Store} this
8657          * @param {Roo.data.Record} record The Record that was updated
8658          * @param {String} operation The update operation being performed.  Value may be one of:
8659          * <pre><code>
8660  Roo.data.Record.EDIT
8661  Roo.data.Record.REJECT
8662  Roo.data.Record.COMMIT
8663          * </code></pre>
8664          */
8665         update : true,
8666         /**
8667          * @event clear
8668          * Fires when the data cache has been cleared.
8669          * @param {Store} this
8670          */
8671         clear : true,
8672         /**
8673          * @event beforeload
8674          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8675          * the load action will be canceled.
8676          * @param {Store} this
8677          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8678          */
8679         beforeload : true,
8680         /**
8681          * @event beforeloadadd
8682          * Fires after a new set of Records has been loaded.
8683          * @param {Store} this
8684          * @param {Roo.data.Record[]} records The Records that were loaded
8685          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8686          */
8687         beforeloadadd : true,
8688         /**
8689          * @event load
8690          * Fires after a new set of Records has been loaded, before they are added to the store.
8691          * @param {Store} this
8692          * @param {Roo.data.Record[]} records The Records that were loaded
8693          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8694          * @params {Object} return from reader
8695          */
8696         load : true,
8697         /**
8698          * @event loadexception
8699          * Fires if an exception occurs in the Proxy during loading.
8700          * Called with the signature of the Proxy's "loadexception" event.
8701          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8702          * 
8703          * @param {Proxy} 
8704          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8705          * @param {Object} load options 
8706          * @param {Object} jsonData from your request (normally this contains the Exception)
8707          */
8708         loadexception : true
8709     });
8710     
8711     if(this.proxy){
8712         this.proxy = Roo.factory(this.proxy, Roo.data);
8713         this.proxy.xmodule = this.xmodule || false;
8714         this.relayEvents(this.proxy,  ["loadexception"]);
8715     }
8716     this.sortToggle = {};
8717     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8718
8719     Roo.data.Store.superclass.constructor.call(this);
8720
8721     if(this.inlineData){
8722         this.loadData(this.inlineData);
8723         delete this.inlineData;
8724     }
8725 };
8726
8727 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8728      /**
8729     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8730     * without a remote query - used by combo/forms at present.
8731     */
8732     
8733     /**
8734     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8735     */
8736     /**
8737     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8738     */
8739     /**
8740     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8741     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8742     */
8743     /**
8744     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8745     * on any HTTP request
8746     */
8747     /**
8748     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8749     */
8750     /**
8751     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8752     */
8753     multiSort: false,
8754     /**
8755     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8756     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8757     */
8758     remoteSort : false,
8759
8760     /**
8761     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8762      * loaded or when a record is removed. (defaults to false).
8763     */
8764     pruneModifiedRecords : false,
8765
8766     // private
8767     lastOptions : null,
8768
8769     /**
8770      * Add Records to the Store and fires the add event.
8771      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8772      */
8773     add : function(records){
8774         records = [].concat(records);
8775         for(var i = 0, len = records.length; i < len; i++){
8776             records[i].join(this);
8777         }
8778         var index = this.data.length;
8779         this.data.addAll(records);
8780         this.fireEvent("add", this, records, index);
8781     },
8782
8783     /**
8784      * Remove a Record from the Store and fires the remove event.
8785      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8786      */
8787     remove : function(record){
8788         var index = this.data.indexOf(record);
8789         this.data.removeAt(index);
8790         if(this.pruneModifiedRecords){
8791             this.modified.remove(record);
8792         }
8793         this.fireEvent("remove", this, record, index);
8794     },
8795
8796     /**
8797      * Remove all Records from the Store and fires the clear event.
8798      */
8799     removeAll : function(){
8800         this.data.clear();
8801         if(this.pruneModifiedRecords){
8802             this.modified = [];
8803         }
8804         this.fireEvent("clear", this);
8805     },
8806
8807     /**
8808      * Inserts Records to the Store at the given index and fires the add event.
8809      * @param {Number} index The start index at which to insert the passed Records.
8810      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8811      */
8812     insert : function(index, records){
8813         records = [].concat(records);
8814         for(var i = 0, len = records.length; i < len; i++){
8815             this.data.insert(index, records[i]);
8816             records[i].join(this);
8817         }
8818         this.fireEvent("add", this, records, index);
8819     },
8820
8821     /**
8822      * Get the index within the cache of the passed Record.
8823      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8824      * @return {Number} The index of the passed Record. Returns -1 if not found.
8825      */
8826     indexOf : function(record){
8827         return this.data.indexOf(record);
8828     },
8829
8830     /**
8831      * Get the index within the cache of the Record with the passed id.
8832      * @param {String} id The id of the Record to find.
8833      * @return {Number} The index of the Record. Returns -1 if not found.
8834      */
8835     indexOfId : function(id){
8836         return this.data.indexOfKey(id);
8837     },
8838
8839     /**
8840      * Get the Record with the specified id.
8841      * @param {String} id The id of the Record to find.
8842      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8843      */
8844     getById : function(id){
8845         return this.data.key(id);
8846     },
8847
8848     /**
8849      * Get the Record at the specified index.
8850      * @param {Number} index The index of the Record to find.
8851      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8852      */
8853     getAt : function(index){
8854         return this.data.itemAt(index);
8855     },
8856
8857     /**
8858      * Returns a range of Records between specified indices.
8859      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8860      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8861      * @return {Roo.data.Record[]} An array of Records
8862      */
8863     getRange : function(start, end){
8864         return this.data.getRange(start, end);
8865     },
8866
8867     // private
8868     storeOptions : function(o){
8869         o = Roo.apply({}, o);
8870         delete o.callback;
8871         delete o.scope;
8872         this.lastOptions = o;
8873     },
8874
8875     /**
8876      * Loads the Record cache from the configured Proxy using the configured Reader.
8877      * <p>
8878      * If using remote paging, then the first load call must specify the <em>start</em>
8879      * and <em>limit</em> properties in the options.params property to establish the initial
8880      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8881      * <p>
8882      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8883      * and this call will return before the new data has been loaded. Perform any post-processing
8884      * in a callback function, or in a "load" event handler.</strong>
8885      * <p>
8886      * @param {Object} options An object containing properties which control loading options:<ul>
8887      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8888      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8889      * passed the following arguments:<ul>
8890      * <li>r : Roo.data.Record[]</li>
8891      * <li>options: Options object from the load call</li>
8892      * <li>success: Boolean success indicator</li></ul></li>
8893      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8894      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8895      * </ul>
8896      */
8897     load : function(options){
8898         options = options || {};
8899         if(this.fireEvent("beforeload", this, options) !== false){
8900             this.storeOptions(options);
8901             var p = Roo.apply(options.params || {}, this.baseParams);
8902             // if meta was not loaded from remote source.. try requesting it.
8903             if (!this.reader.metaFromRemote) {
8904                 p._requestMeta = 1;
8905             }
8906             if(this.sortInfo && this.remoteSort){
8907                 var pn = this.paramNames;
8908                 p[pn["sort"]] = this.sortInfo.field;
8909                 p[pn["dir"]] = this.sortInfo.direction;
8910             }
8911             if (this.multiSort) {
8912                 var pn = this.paramNames;
8913                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8914             }
8915             
8916             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8917         }
8918     },
8919
8920     /**
8921      * Reloads the Record cache from the configured Proxy using the configured Reader and
8922      * the options from the last load operation performed.
8923      * @param {Object} options (optional) An object containing properties which may override the options
8924      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8925      * the most recently used options are reused).
8926      */
8927     reload : function(options){
8928         this.load(Roo.applyIf(options||{}, this.lastOptions));
8929     },
8930
8931     // private
8932     // Called as a callback by the Reader during a load operation.
8933     loadRecords : function(o, options, success){
8934         if(!o || success === false){
8935             if(success !== false){
8936                 this.fireEvent("load", this, [], options, o);
8937             }
8938             if(options.callback){
8939                 options.callback.call(options.scope || this, [], options, false);
8940             }
8941             return;
8942         }
8943         // if data returned failure - throw an exception.
8944         if (o.success === false) {
8945             // show a message if no listener is registered.
8946             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8947                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8948             }
8949             // loadmask wil be hooked into this..
8950             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8951             return;
8952         }
8953         var r = o.records, t = o.totalRecords || r.length;
8954         
8955         this.fireEvent("beforeloadadd", this, r, options, o);
8956         
8957         if(!options || options.add !== true){
8958             if(this.pruneModifiedRecords){
8959                 this.modified = [];
8960             }
8961             for(var i = 0, len = r.length; i < len; i++){
8962                 r[i].join(this);
8963             }
8964             if(this.snapshot){
8965                 this.data = this.snapshot;
8966                 delete this.snapshot;
8967             }
8968             this.data.clear();
8969             this.data.addAll(r);
8970             this.totalLength = t;
8971             this.applySort();
8972             this.fireEvent("datachanged", this);
8973         }else{
8974             this.totalLength = Math.max(t, this.data.length+r.length);
8975             this.add(r);
8976         }
8977         this.fireEvent("load", this, r, options, o);
8978         if(options.callback){
8979             options.callback.call(options.scope || this, r, options, true);
8980         }
8981     },
8982
8983
8984     /**
8985      * Loads data from a passed data block. A Reader which understands the format of the data
8986      * must have been configured in the constructor.
8987      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8988      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8989      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8990      */
8991     loadData : function(o, append){
8992         var r = this.reader.readRecords(o);
8993         this.loadRecords(r, {add: append}, true);
8994     },
8995
8996     /**
8997      * Gets the number of cached records.
8998      * <p>
8999      * <em>If using paging, this may not be the total size of the dataset. If the data object
9000      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9001      * the data set size</em>
9002      */
9003     getCount : function(){
9004         return this.data.length || 0;
9005     },
9006
9007     /**
9008      * Gets the total number of records in the dataset as returned by the server.
9009      * <p>
9010      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9011      * the dataset size</em>
9012      */
9013     getTotalCount : function(){
9014         return this.totalLength || 0;
9015     },
9016
9017     /**
9018      * Returns the sort state of the Store as an object with two properties:
9019      * <pre><code>
9020  field {String} The name of the field by which the Records are sorted
9021  direction {String} The sort order, "ASC" or "DESC"
9022      * </code></pre>
9023      */
9024     getSortState : function(){
9025         return this.sortInfo;
9026     },
9027
9028     // private
9029     applySort : function(){
9030         if(this.sortInfo && !this.remoteSort){
9031             var s = this.sortInfo, f = s.field;
9032             var st = this.fields.get(f).sortType;
9033             var fn = function(r1, r2){
9034                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9035                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9036             };
9037             this.data.sort(s.direction, fn);
9038             if(this.snapshot && this.snapshot != this.data){
9039                 this.snapshot.sort(s.direction, fn);
9040             }
9041         }
9042     },
9043
9044     /**
9045      * Sets the default sort column and order to be used by the next load operation.
9046      * @param {String} fieldName The name of the field to sort by.
9047      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9048      */
9049     setDefaultSort : function(field, dir){
9050         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9051     },
9052
9053     /**
9054      * Sort the Records.
9055      * If remote sorting is used, the sort is performed on the server, and the cache is
9056      * reloaded. If local sorting is used, the cache is sorted internally.
9057      * @param {String} fieldName The name of the field to sort by.
9058      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9059      */
9060     sort : function(fieldName, dir){
9061         var f = this.fields.get(fieldName);
9062         if(!dir){
9063             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9064             
9065             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9066                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9067             }else{
9068                 dir = f.sortDir;
9069             }
9070         }
9071         this.sortToggle[f.name] = dir;
9072         this.sortInfo = {field: f.name, direction: dir};
9073         if(!this.remoteSort){
9074             this.applySort();
9075             this.fireEvent("datachanged", this);
9076         }else{
9077             this.load(this.lastOptions);
9078         }
9079     },
9080
9081     /**
9082      * Calls the specified function for each of the Records in the cache.
9083      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9084      * Returning <em>false</em> aborts and exits the iteration.
9085      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9086      */
9087     each : function(fn, scope){
9088         this.data.each(fn, scope);
9089     },
9090
9091     /**
9092      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9093      * (e.g., during paging).
9094      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9095      */
9096     getModifiedRecords : function(){
9097         return this.modified;
9098     },
9099
9100     // private
9101     createFilterFn : function(property, value, anyMatch){
9102         if(!value.exec){ // not a regex
9103             value = String(value);
9104             if(value.length == 0){
9105                 return false;
9106             }
9107             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9108         }
9109         return function(r){
9110             return value.test(r.data[property]);
9111         };
9112     },
9113
9114     /**
9115      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9116      * @param {String} property A field on your records
9117      * @param {Number} start The record index to start at (defaults to 0)
9118      * @param {Number} end The last record index to include (defaults to length - 1)
9119      * @return {Number} The sum
9120      */
9121     sum : function(property, start, end){
9122         var rs = this.data.items, v = 0;
9123         start = start || 0;
9124         end = (end || end === 0) ? end : rs.length-1;
9125
9126         for(var i = start; i <= end; i++){
9127             v += (rs[i].data[property] || 0);
9128         }
9129         return v;
9130     },
9131
9132     /**
9133      * Filter the records by a specified property.
9134      * @param {String} field A field on your records
9135      * @param {String/RegExp} value Either a string that the field
9136      * should start with or a RegExp to test against the field
9137      * @param {Boolean} anyMatch True to match any part not just the beginning
9138      */
9139     filter : function(property, value, anyMatch){
9140         var fn = this.createFilterFn(property, value, anyMatch);
9141         return fn ? this.filterBy(fn) : this.clearFilter();
9142     },
9143
9144     /**
9145      * Filter by a function. The specified function will be called with each
9146      * record in this data source. If the function returns true the record is included,
9147      * otherwise it is filtered.
9148      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9149      * @param {Object} scope (optional) The scope of the function (defaults to this)
9150      */
9151     filterBy : function(fn, scope){
9152         this.snapshot = this.snapshot || this.data;
9153         this.data = this.queryBy(fn, scope||this);
9154         this.fireEvent("datachanged", this);
9155     },
9156
9157     /**
9158      * Query the records by a specified property.
9159      * @param {String} field A field on your records
9160      * @param {String/RegExp} value Either a string that the field
9161      * should start with or a RegExp to test against the field
9162      * @param {Boolean} anyMatch True to match any part not just the beginning
9163      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9164      */
9165     query : function(property, value, anyMatch){
9166         var fn = this.createFilterFn(property, value, anyMatch);
9167         return fn ? this.queryBy(fn) : this.data.clone();
9168     },
9169
9170     /**
9171      * Query by a function. The specified function will be called with each
9172      * record in this data source. If the function returns true the record is included
9173      * in the results.
9174      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9175      * @param {Object} scope (optional) The scope of the function (defaults to this)
9176       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9177      **/
9178     queryBy : function(fn, scope){
9179         var data = this.snapshot || this.data;
9180         return data.filterBy(fn, scope||this);
9181     },
9182
9183     /**
9184      * Collects unique values for a particular dataIndex from this store.
9185      * @param {String} dataIndex The property to collect
9186      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9187      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9188      * @return {Array} An array of the unique values
9189      **/
9190     collect : function(dataIndex, allowNull, bypassFilter){
9191         var d = (bypassFilter === true && this.snapshot) ?
9192                 this.snapshot.items : this.data.items;
9193         var v, sv, r = [], l = {};
9194         for(var i = 0, len = d.length; i < len; i++){
9195             v = d[i].data[dataIndex];
9196             sv = String(v);
9197             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9198                 l[sv] = true;
9199                 r[r.length] = v;
9200             }
9201         }
9202         return r;
9203     },
9204
9205     /**
9206      * Revert to a view of the Record cache with no filtering applied.
9207      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9208      */
9209     clearFilter : function(suppressEvent){
9210         if(this.snapshot && this.snapshot != this.data){
9211             this.data = this.snapshot;
9212             delete this.snapshot;
9213             if(suppressEvent !== true){
9214                 this.fireEvent("datachanged", this);
9215             }
9216         }
9217     },
9218
9219     // private
9220     afterEdit : function(record){
9221         if(this.modified.indexOf(record) == -1){
9222             this.modified.push(record);
9223         }
9224         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9225     },
9226     
9227     // private
9228     afterReject : function(record){
9229         this.modified.remove(record);
9230         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9231     },
9232
9233     // private
9234     afterCommit : function(record){
9235         this.modified.remove(record);
9236         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9237     },
9238
9239     /**
9240      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9241      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9242      */
9243     commitChanges : function(){
9244         var m = this.modified.slice(0);
9245         this.modified = [];
9246         for(var i = 0, len = m.length; i < len; i++){
9247             m[i].commit();
9248         }
9249     },
9250
9251     /**
9252      * Cancel outstanding changes on all changed records.
9253      */
9254     rejectChanges : function(){
9255         var m = this.modified.slice(0);
9256         this.modified = [];
9257         for(var i = 0, len = m.length; i < len; i++){
9258             m[i].reject();
9259         }
9260     },
9261
9262     onMetaChange : function(meta, rtype, o){
9263         this.recordType = rtype;
9264         this.fields = rtype.prototype.fields;
9265         delete this.snapshot;
9266         this.sortInfo = meta.sortInfo || this.sortInfo;
9267         this.modified = [];
9268         this.fireEvent('metachange', this, this.reader.meta);
9269     },
9270     
9271     moveIndex : function(data, type)
9272     {
9273         var index = this.indexOf(data);
9274         
9275         var newIndex = index + type;
9276         
9277         this.remove(data);
9278         
9279         this.insert(newIndex, data);
9280         
9281     }
9282 });/*
9283  * Based on:
9284  * Ext JS Library 1.1.1
9285  * Copyright(c) 2006-2007, Ext JS, LLC.
9286  *
9287  * Originally Released Under LGPL - original licence link has changed is not relivant.
9288  *
9289  * Fork - LGPL
9290  * <script type="text/javascript">
9291  */
9292
9293 /**
9294  * @class Roo.data.SimpleStore
9295  * @extends Roo.data.Store
9296  * Small helper class to make creating Stores from Array data easier.
9297  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9298  * @cfg {Array} fields An array of field definition objects, or field name strings.
9299  * @cfg {Array} data The multi-dimensional array of data
9300  * @constructor
9301  * @param {Object} config
9302  */
9303 Roo.data.SimpleStore = function(config){
9304     Roo.data.SimpleStore.superclass.constructor.call(this, {
9305         isLocal : true,
9306         reader: new Roo.data.ArrayReader({
9307                 id: config.id
9308             },
9309             Roo.data.Record.create(config.fields)
9310         ),
9311         proxy : new Roo.data.MemoryProxy(config.data)
9312     });
9313     this.load();
9314 };
9315 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9316  * Based on:
9317  * Ext JS Library 1.1.1
9318  * Copyright(c) 2006-2007, Ext JS, LLC.
9319  *
9320  * Originally Released Under LGPL - original licence link has changed is not relivant.
9321  *
9322  * Fork - LGPL
9323  * <script type="text/javascript">
9324  */
9325
9326 /**
9327 /**
9328  * @extends Roo.data.Store
9329  * @class Roo.data.JsonStore
9330  * Small helper class to make creating Stores for JSON data easier. <br/>
9331 <pre><code>
9332 var store = new Roo.data.JsonStore({
9333     url: 'get-images.php',
9334     root: 'images',
9335     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9336 });
9337 </code></pre>
9338  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9339  * JsonReader and HttpProxy (unless inline data is provided).</b>
9340  * @cfg {Array} fields An array of field definition objects, or field name strings.
9341  * @constructor
9342  * @param {Object} config
9343  */
9344 Roo.data.JsonStore = function(c){
9345     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9346         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9347         reader: new Roo.data.JsonReader(c, c.fields)
9348     }));
9349 };
9350 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9351  * Based on:
9352  * Ext JS Library 1.1.1
9353  * Copyright(c) 2006-2007, Ext JS, LLC.
9354  *
9355  * Originally Released Under LGPL - original licence link has changed is not relivant.
9356  *
9357  * Fork - LGPL
9358  * <script type="text/javascript">
9359  */
9360
9361  
9362 Roo.data.Field = function(config){
9363     if(typeof config == "string"){
9364         config = {name: config};
9365     }
9366     Roo.apply(this, config);
9367     
9368     if(!this.type){
9369         this.type = "auto";
9370     }
9371     
9372     var st = Roo.data.SortTypes;
9373     // named sortTypes are supported, here we look them up
9374     if(typeof this.sortType == "string"){
9375         this.sortType = st[this.sortType];
9376     }
9377     
9378     // set default sortType for strings and dates
9379     if(!this.sortType){
9380         switch(this.type){
9381             case "string":
9382                 this.sortType = st.asUCString;
9383                 break;
9384             case "date":
9385                 this.sortType = st.asDate;
9386                 break;
9387             default:
9388                 this.sortType = st.none;
9389         }
9390     }
9391
9392     // define once
9393     var stripRe = /[\$,%]/g;
9394
9395     // prebuilt conversion function for this field, instead of
9396     // switching every time we're reading a value
9397     if(!this.convert){
9398         var cv, dateFormat = this.dateFormat;
9399         switch(this.type){
9400             case "":
9401             case "auto":
9402             case undefined:
9403                 cv = function(v){ return v; };
9404                 break;
9405             case "string":
9406                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9407                 break;
9408             case "int":
9409                 cv = function(v){
9410                     return v !== undefined && v !== null && v !== '' ?
9411                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9412                     };
9413                 break;
9414             case "float":
9415                 cv = function(v){
9416                     return v !== undefined && v !== null && v !== '' ?
9417                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9418                     };
9419                 break;
9420             case "bool":
9421             case "boolean":
9422                 cv = function(v){ return v === true || v === "true" || v == 1; };
9423                 break;
9424             case "date":
9425                 cv = function(v){
9426                     if(!v){
9427                         return '';
9428                     }
9429                     if(v instanceof Date){
9430                         return v;
9431                     }
9432                     if(dateFormat){
9433                         if(dateFormat == "timestamp"){
9434                             return new Date(v*1000);
9435                         }
9436                         return Date.parseDate(v, dateFormat);
9437                     }
9438                     var parsed = Date.parse(v);
9439                     return parsed ? new Date(parsed) : null;
9440                 };
9441              break;
9442             
9443         }
9444         this.convert = cv;
9445     }
9446 };
9447
9448 Roo.data.Field.prototype = {
9449     dateFormat: null,
9450     defaultValue: "",
9451     mapping: null,
9452     sortType : null,
9453     sortDir : "ASC"
9454 };/*
9455  * Based on:
9456  * Ext JS Library 1.1.1
9457  * Copyright(c) 2006-2007, Ext JS, LLC.
9458  *
9459  * Originally Released Under LGPL - original licence link has changed is not relivant.
9460  *
9461  * Fork - LGPL
9462  * <script type="text/javascript">
9463  */
9464  
9465 // Base class for reading structured data from a data source.  This class is intended to be
9466 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9467
9468 /**
9469  * @class Roo.data.DataReader
9470  * Base class for reading structured data from a data source.  This class is intended to be
9471  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9472  */
9473
9474 Roo.data.DataReader = function(meta, recordType){
9475     
9476     this.meta = meta;
9477     
9478     this.recordType = recordType instanceof Array ? 
9479         Roo.data.Record.create(recordType) : recordType;
9480 };
9481
9482 Roo.data.DataReader.prototype = {
9483      /**
9484      * Create an empty record
9485      * @param {Object} data (optional) - overlay some values
9486      * @return {Roo.data.Record} record created.
9487      */
9488     newRow :  function(d) {
9489         var da =  {};
9490         this.recordType.prototype.fields.each(function(c) {
9491             switch( c.type) {
9492                 case 'int' : da[c.name] = 0; break;
9493                 case 'date' : da[c.name] = new Date(); break;
9494                 case 'float' : da[c.name] = 0.0; break;
9495                 case 'boolean' : da[c.name] = false; break;
9496                 default : da[c.name] = ""; break;
9497             }
9498             
9499         });
9500         return new this.recordType(Roo.apply(da, d));
9501     }
9502     
9503 };/*
9504  * Based on:
9505  * Ext JS Library 1.1.1
9506  * Copyright(c) 2006-2007, Ext JS, LLC.
9507  *
9508  * Originally Released Under LGPL - original licence link has changed is not relivant.
9509  *
9510  * Fork - LGPL
9511  * <script type="text/javascript">
9512  */
9513
9514 /**
9515  * @class Roo.data.DataProxy
9516  * @extends Roo.data.Observable
9517  * This class is an abstract base class for implementations which provide retrieval of
9518  * unformatted data objects.<br>
9519  * <p>
9520  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9521  * (of the appropriate type which knows how to parse the data object) to provide a block of
9522  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9523  * <p>
9524  * Custom implementations must implement the load method as described in
9525  * {@link Roo.data.HttpProxy#load}.
9526  */
9527 Roo.data.DataProxy = function(){
9528     this.addEvents({
9529         /**
9530          * @event beforeload
9531          * Fires before a network request is made to retrieve a data object.
9532          * @param {Object} This DataProxy object.
9533          * @param {Object} params The params parameter to the load function.
9534          */
9535         beforeload : true,
9536         /**
9537          * @event load
9538          * Fires before the load method's callback is called.
9539          * @param {Object} This DataProxy object.
9540          * @param {Object} o The data object.
9541          * @param {Object} arg The callback argument object passed to the load function.
9542          */
9543         load : true,
9544         /**
9545          * @event loadexception
9546          * Fires if an Exception occurs during data retrieval.
9547          * @param {Object} This DataProxy object.
9548          * @param {Object} o The data object.
9549          * @param {Object} arg The callback argument object passed to the load function.
9550          * @param {Object} e The Exception.
9551          */
9552         loadexception : true
9553     });
9554     Roo.data.DataProxy.superclass.constructor.call(this);
9555 };
9556
9557 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9558
9559     /**
9560      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9561      */
9562 /*
9563  * Based on:
9564  * Ext JS Library 1.1.1
9565  * Copyright(c) 2006-2007, Ext JS, LLC.
9566  *
9567  * Originally Released Under LGPL - original licence link has changed is not relivant.
9568  *
9569  * Fork - LGPL
9570  * <script type="text/javascript">
9571  */
9572 /**
9573  * @class Roo.data.MemoryProxy
9574  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9575  * to the Reader when its load method is called.
9576  * @constructor
9577  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9578  */
9579 Roo.data.MemoryProxy = function(data){
9580     if (data.data) {
9581         data = data.data;
9582     }
9583     Roo.data.MemoryProxy.superclass.constructor.call(this);
9584     this.data = data;
9585 };
9586
9587 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9588     /**
9589      * Load data from the requested source (in this case an in-memory
9590      * data object passed to the constructor), read the data object into
9591      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9592      * process that block using the passed callback.
9593      * @param {Object} params This parameter is not used by the MemoryProxy class.
9594      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9595      * object into a block of Roo.data.Records.
9596      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9597      * The function must be passed <ul>
9598      * <li>The Record block object</li>
9599      * <li>The "arg" argument from the load function</li>
9600      * <li>A boolean success indicator</li>
9601      * </ul>
9602      * @param {Object} scope The scope in which to call the callback
9603      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9604      */
9605     load : function(params, reader, callback, scope, arg){
9606         params = params || {};
9607         var result;
9608         try {
9609             result = reader.readRecords(this.data);
9610         }catch(e){
9611             this.fireEvent("loadexception", this, arg, null, e);
9612             callback.call(scope, null, arg, false);
9613             return;
9614         }
9615         callback.call(scope, result, arg, true);
9616     },
9617     
9618     // private
9619     update : function(params, records){
9620         
9621     }
9622 });/*
9623  * Based on:
9624  * Ext JS Library 1.1.1
9625  * Copyright(c) 2006-2007, Ext JS, LLC.
9626  *
9627  * Originally Released Under LGPL - original licence link has changed is not relivant.
9628  *
9629  * Fork - LGPL
9630  * <script type="text/javascript">
9631  */
9632 /**
9633  * @class Roo.data.HttpProxy
9634  * @extends Roo.data.DataProxy
9635  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9636  * configured to reference a certain URL.<br><br>
9637  * <p>
9638  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9639  * from which the running page was served.<br><br>
9640  * <p>
9641  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9642  * <p>
9643  * Be aware that to enable the browser to parse an XML document, the server must set
9644  * the Content-Type header in the HTTP response to "text/xml".
9645  * @constructor
9646  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9647  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9648  * will be used to make the request.
9649  */
9650 Roo.data.HttpProxy = function(conn){
9651     Roo.data.HttpProxy.superclass.constructor.call(this);
9652     // is conn a conn config or a real conn?
9653     this.conn = conn;
9654     this.useAjax = !conn || !conn.events;
9655   
9656 };
9657
9658 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9659     // thse are take from connection...
9660     
9661     /**
9662      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9663      */
9664     /**
9665      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9666      * extra parameters to each request made by this object. (defaults to undefined)
9667      */
9668     /**
9669      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9670      *  to each request made by this object. (defaults to undefined)
9671      */
9672     /**
9673      * @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)
9674      */
9675     /**
9676      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9677      */
9678      /**
9679      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9680      * @type Boolean
9681      */
9682   
9683
9684     /**
9685      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9686      * @type Boolean
9687      */
9688     /**
9689      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9690      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9691      * a finer-grained basis than the DataProxy events.
9692      */
9693     getConnection : function(){
9694         return this.useAjax ? Roo.Ajax : this.conn;
9695     },
9696
9697     /**
9698      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9699      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9700      * process that block using the passed callback.
9701      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9702      * for the request to the remote server.
9703      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9704      * object into a block of Roo.data.Records.
9705      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9706      * The function must be passed <ul>
9707      * <li>The Record block object</li>
9708      * <li>The "arg" argument from the load function</li>
9709      * <li>A boolean success indicator</li>
9710      * </ul>
9711      * @param {Object} scope The scope in which to call the callback
9712      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9713      */
9714     load : function(params, reader, callback, scope, arg){
9715         if(this.fireEvent("beforeload", this, params) !== false){
9716             var  o = {
9717                 params : params || {},
9718                 request: {
9719                     callback : callback,
9720                     scope : scope,
9721                     arg : arg
9722                 },
9723                 reader: reader,
9724                 callback : this.loadResponse,
9725                 scope: this
9726             };
9727             if(this.useAjax){
9728                 Roo.applyIf(o, this.conn);
9729                 if(this.activeRequest){
9730                     Roo.Ajax.abort(this.activeRequest);
9731                 }
9732                 this.activeRequest = Roo.Ajax.request(o);
9733             }else{
9734                 this.conn.request(o);
9735             }
9736         }else{
9737             callback.call(scope||this, null, arg, false);
9738         }
9739     },
9740
9741     // private
9742     loadResponse : function(o, success, response){
9743         delete this.activeRequest;
9744         if(!success){
9745             this.fireEvent("loadexception", this, o, response);
9746             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9747             return;
9748         }
9749         var result;
9750         try {
9751             result = o.reader.read(response);
9752         }catch(e){
9753             this.fireEvent("loadexception", this, o, response, e);
9754             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9755             return;
9756         }
9757         
9758         this.fireEvent("load", this, o, o.request.arg);
9759         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9760     },
9761
9762     // private
9763     update : function(dataSet){
9764
9765     },
9766
9767     // private
9768     updateResponse : function(dataSet){
9769
9770     }
9771 });/*
9772  * Based on:
9773  * Ext JS Library 1.1.1
9774  * Copyright(c) 2006-2007, Ext JS, LLC.
9775  *
9776  * Originally Released Under LGPL - original licence link has changed is not relivant.
9777  *
9778  * Fork - LGPL
9779  * <script type="text/javascript">
9780  */
9781
9782 /**
9783  * @class Roo.data.ScriptTagProxy
9784  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9785  * other than the originating domain of the running page.<br><br>
9786  * <p>
9787  * <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
9788  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9789  * <p>
9790  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9791  * source code that is used as the source inside a &lt;script> tag.<br><br>
9792  * <p>
9793  * In order for the browser to process the returned data, the server must wrap the data object
9794  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9795  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9796  * depending on whether the callback name was passed:
9797  * <p>
9798  * <pre><code>
9799 boolean scriptTag = false;
9800 String cb = request.getParameter("callback");
9801 if (cb != null) {
9802     scriptTag = true;
9803     response.setContentType("text/javascript");
9804 } else {
9805     response.setContentType("application/x-json");
9806 }
9807 Writer out = response.getWriter();
9808 if (scriptTag) {
9809     out.write(cb + "(");
9810 }
9811 out.print(dataBlock.toJsonString());
9812 if (scriptTag) {
9813     out.write(");");
9814 }
9815 </pre></code>
9816  *
9817  * @constructor
9818  * @param {Object} config A configuration object.
9819  */
9820 Roo.data.ScriptTagProxy = function(config){
9821     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9822     Roo.apply(this, config);
9823     this.head = document.getElementsByTagName("head")[0];
9824 };
9825
9826 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9827
9828 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9829     /**
9830      * @cfg {String} url The URL from which to request the data object.
9831      */
9832     /**
9833      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9834      */
9835     timeout : 30000,
9836     /**
9837      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9838      * the server the name of the callback function set up by the load call to process the returned data object.
9839      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9840      * javascript output which calls this named function passing the data object as its only parameter.
9841      */
9842     callbackParam : "callback",
9843     /**
9844      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9845      * name to the request.
9846      */
9847     nocache : true,
9848
9849     /**
9850      * Load data from the configured URL, read the data object into
9851      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9852      * process that block using the passed callback.
9853      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9854      * for the request to the remote server.
9855      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9856      * object into a block of Roo.data.Records.
9857      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9858      * The function must be passed <ul>
9859      * <li>The Record block object</li>
9860      * <li>The "arg" argument from the load function</li>
9861      * <li>A boolean success indicator</li>
9862      * </ul>
9863      * @param {Object} scope The scope in which to call the callback
9864      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9865      */
9866     load : function(params, reader, callback, scope, arg){
9867         if(this.fireEvent("beforeload", this, params) !== false){
9868
9869             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9870
9871             var url = this.url;
9872             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9873             if(this.nocache){
9874                 url += "&_dc=" + (new Date().getTime());
9875             }
9876             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9877             var trans = {
9878                 id : transId,
9879                 cb : "stcCallback"+transId,
9880                 scriptId : "stcScript"+transId,
9881                 params : params,
9882                 arg : arg,
9883                 url : url,
9884                 callback : callback,
9885                 scope : scope,
9886                 reader : reader
9887             };
9888             var conn = this;
9889
9890             window[trans.cb] = function(o){
9891                 conn.handleResponse(o, trans);
9892             };
9893
9894             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9895
9896             if(this.autoAbort !== false){
9897                 this.abort();
9898             }
9899
9900             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9901
9902             var script = document.createElement("script");
9903             script.setAttribute("src", url);
9904             script.setAttribute("type", "text/javascript");
9905             script.setAttribute("id", trans.scriptId);
9906             this.head.appendChild(script);
9907
9908             this.trans = trans;
9909         }else{
9910             callback.call(scope||this, null, arg, false);
9911         }
9912     },
9913
9914     // private
9915     isLoading : function(){
9916         return this.trans ? true : false;
9917     },
9918
9919     /**
9920      * Abort the current server request.
9921      */
9922     abort : function(){
9923         if(this.isLoading()){
9924             this.destroyTrans(this.trans);
9925         }
9926     },
9927
9928     // private
9929     destroyTrans : function(trans, isLoaded){
9930         this.head.removeChild(document.getElementById(trans.scriptId));
9931         clearTimeout(trans.timeoutId);
9932         if(isLoaded){
9933             window[trans.cb] = undefined;
9934             try{
9935                 delete window[trans.cb];
9936             }catch(e){}
9937         }else{
9938             // if hasn't been loaded, wait for load to remove it to prevent script error
9939             window[trans.cb] = function(){
9940                 window[trans.cb] = undefined;
9941                 try{
9942                     delete window[trans.cb];
9943                 }catch(e){}
9944             };
9945         }
9946     },
9947
9948     // private
9949     handleResponse : function(o, trans){
9950         this.trans = false;
9951         this.destroyTrans(trans, true);
9952         var result;
9953         try {
9954             result = trans.reader.readRecords(o);
9955         }catch(e){
9956             this.fireEvent("loadexception", this, o, trans.arg, e);
9957             trans.callback.call(trans.scope||window, null, trans.arg, false);
9958             return;
9959         }
9960         this.fireEvent("load", this, o, trans.arg);
9961         trans.callback.call(trans.scope||window, result, trans.arg, true);
9962     },
9963
9964     // private
9965     handleFailure : function(trans){
9966         this.trans = false;
9967         this.destroyTrans(trans, false);
9968         this.fireEvent("loadexception", this, null, trans.arg);
9969         trans.callback.call(trans.scope||window, null, trans.arg, false);
9970     }
9971 });/*
9972  * Based on:
9973  * Ext JS Library 1.1.1
9974  * Copyright(c) 2006-2007, Ext JS, LLC.
9975  *
9976  * Originally Released Under LGPL - original licence link has changed is not relivant.
9977  *
9978  * Fork - LGPL
9979  * <script type="text/javascript">
9980  */
9981
9982 /**
9983  * @class Roo.data.JsonReader
9984  * @extends Roo.data.DataReader
9985  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9986  * based on mappings in a provided Roo.data.Record constructor.
9987  * 
9988  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9989  * in the reply previously. 
9990  * 
9991  * <p>
9992  * Example code:
9993  * <pre><code>
9994 var RecordDef = Roo.data.Record.create([
9995     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9996     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9997 ]);
9998 var myReader = new Roo.data.JsonReader({
9999     totalProperty: "results",    // The property which contains the total dataset size (optional)
10000     root: "rows",                // The property which contains an Array of row objects
10001     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10002 }, RecordDef);
10003 </code></pre>
10004  * <p>
10005  * This would consume a JSON file like this:
10006  * <pre><code>
10007 { 'results': 2, 'rows': [
10008     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10009     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10010 }
10011 </code></pre>
10012  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10013  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10014  * paged from the remote server.
10015  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10016  * @cfg {String} root name of the property which contains the Array of row objects.
10017  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10018  * @constructor
10019  * Create a new JsonReader
10020  * @param {Object} meta Metadata configuration options
10021  * @param {Object} recordType Either an Array of field definition objects,
10022  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10023  */
10024 Roo.data.JsonReader = function(meta, recordType){
10025     
10026     meta = meta || {};
10027     // set some defaults:
10028     Roo.applyIf(meta, {
10029         totalProperty: 'total',
10030         successProperty : 'success',
10031         root : 'data',
10032         id : 'id'
10033     });
10034     
10035     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10036 };
10037 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10038     
10039     /**
10040      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10041      * Used by Store query builder to append _requestMeta to params.
10042      * 
10043      */
10044     metaFromRemote : false,
10045     /**
10046      * This method is only used by a DataProxy which has retrieved data from a remote server.
10047      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10048      * @return {Object} data A data block which is used by an Roo.data.Store object as
10049      * a cache of Roo.data.Records.
10050      */
10051     read : function(response){
10052         var json = response.responseText;
10053        
10054         var o = /* eval:var:o */ eval("("+json+")");
10055         if(!o) {
10056             throw {message: "JsonReader.read: Json object not found"};
10057         }
10058         
10059         if(o.metaData){
10060             
10061             delete this.ef;
10062             this.metaFromRemote = true;
10063             this.meta = o.metaData;
10064             this.recordType = Roo.data.Record.create(o.metaData.fields);
10065             this.onMetaChange(this.meta, this.recordType, o);
10066         }
10067         return this.readRecords(o);
10068     },
10069
10070     // private function a store will implement
10071     onMetaChange : function(meta, recordType, o){
10072
10073     },
10074
10075     /**
10076          * @ignore
10077          */
10078     simpleAccess: function(obj, subsc) {
10079         return obj[subsc];
10080     },
10081
10082         /**
10083          * @ignore
10084          */
10085     getJsonAccessor: function(){
10086         var re = /[\[\.]/;
10087         return function(expr) {
10088             try {
10089                 return(re.test(expr))
10090                     ? new Function("obj", "return obj." + expr)
10091                     : function(obj){
10092                         return obj[expr];
10093                     };
10094             } catch(e){}
10095             return Roo.emptyFn;
10096         };
10097     }(),
10098
10099     /**
10100      * Create a data block containing Roo.data.Records from an XML document.
10101      * @param {Object} o An object which contains an Array of row objects in the property specified
10102      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10103      * which contains the total size of the dataset.
10104      * @return {Object} data A data block which is used by an Roo.data.Store object as
10105      * a cache of Roo.data.Records.
10106      */
10107     readRecords : function(o){
10108         /**
10109          * After any data loads, the raw JSON data is available for further custom processing.
10110          * @type Object
10111          */
10112         this.o = o;
10113         var s = this.meta, Record = this.recordType,
10114             f = Record.prototype.fields, fi = f.items, fl = f.length;
10115
10116 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10117         if (!this.ef) {
10118             if(s.totalProperty) {
10119                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10120                 }
10121                 if(s.successProperty) {
10122                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10123                 }
10124                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10125                 if (s.id) {
10126                         var g = this.getJsonAccessor(s.id);
10127                         this.getId = function(rec) {
10128                                 var r = g(rec);
10129                                 return (r === undefined || r === "") ? null : r;
10130                         };
10131                 } else {
10132                         this.getId = function(){return null;};
10133                 }
10134             this.ef = [];
10135             for(var jj = 0; jj < fl; jj++){
10136                 f = fi[jj];
10137                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10138                 this.ef[jj] = this.getJsonAccessor(map);
10139             }
10140         }
10141
10142         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10143         if(s.totalProperty){
10144             var vt = parseInt(this.getTotal(o), 10);
10145             if(!isNaN(vt)){
10146                 totalRecords = vt;
10147             }
10148         }
10149         if(s.successProperty){
10150             var vs = this.getSuccess(o);
10151             if(vs === false || vs === 'false'){
10152                 success = false;
10153             }
10154         }
10155         var records = [];
10156             for(var i = 0; i < c; i++){
10157                     var n = root[i];
10158                 var values = {};
10159                 var id = this.getId(n);
10160                 for(var j = 0; j < fl; j++){
10161                     f = fi[j];
10162                 var v = this.ef[j](n);
10163                 if (!f.convert) {
10164                     Roo.log('missing convert for ' + f.name);
10165                     Roo.log(f);
10166                     continue;
10167                 }
10168                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10169                 }
10170                 var record = new Record(values, id);
10171                 record.json = n;
10172                 records[i] = record;
10173             }
10174             return {
10175             raw : o,
10176                 success : success,
10177                 records : records,
10178                 totalRecords : totalRecords
10179             };
10180     }
10181 });/*
10182  * Based on:
10183  * Ext JS Library 1.1.1
10184  * Copyright(c) 2006-2007, Ext JS, LLC.
10185  *
10186  * Originally Released Under LGPL - original licence link has changed is not relivant.
10187  *
10188  * Fork - LGPL
10189  * <script type="text/javascript">
10190  */
10191
10192 /**
10193  * @class Roo.data.ArrayReader
10194  * @extends Roo.data.DataReader
10195  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10196  * Each element of that Array represents a row of data fields. The
10197  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10198  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10199  * <p>
10200  * Example code:.
10201  * <pre><code>
10202 var RecordDef = Roo.data.Record.create([
10203     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10204     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10205 ]);
10206 var myReader = new Roo.data.ArrayReader({
10207     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10208 }, RecordDef);
10209 </code></pre>
10210  * <p>
10211  * This would consume an Array like this:
10212  * <pre><code>
10213 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10214   </code></pre>
10215  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10216  * @constructor
10217  * Create a new JsonReader
10218  * @param {Object} meta Metadata configuration options.
10219  * @param {Object} recordType Either an Array of field definition objects
10220  * as specified to {@link Roo.data.Record#create},
10221  * or an {@link Roo.data.Record} object
10222  * created using {@link Roo.data.Record#create}.
10223  */
10224 Roo.data.ArrayReader = function(meta, recordType){
10225     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10226 };
10227
10228 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10229     /**
10230      * Create a data block containing Roo.data.Records from an XML document.
10231      * @param {Object} o An Array of row objects which represents the dataset.
10232      * @return {Object} data A data block which is used by an Roo.data.Store object as
10233      * a cache of Roo.data.Records.
10234      */
10235     readRecords : function(o){
10236         var sid = this.meta ? this.meta.id : null;
10237         var recordType = this.recordType, fields = recordType.prototype.fields;
10238         var records = [];
10239         var root = o;
10240             for(var i = 0; i < root.length; i++){
10241                     var n = root[i];
10242                 var values = {};
10243                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10244                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10245                 var f = fields.items[j];
10246                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10247                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10248                 v = f.convert(v);
10249                 values[f.name] = v;
10250             }
10251                 var record = new recordType(values, id);
10252                 record.json = n;
10253                 records[records.length] = record;
10254             }
10255             return {
10256                 records : records,
10257                 totalRecords : records.length
10258             };
10259     }
10260 });/*
10261  * - LGPL
10262  * * 
10263  */
10264
10265 /**
10266  * @class Roo.bootstrap.ComboBox
10267  * @extends Roo.bootstrap.TriggerField
10268  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10269  * @cfg {Boolean} append (true|false) default false
10270  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10271  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10272  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10273  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10274  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10275  * @constructor
10276  * Create a new ComboBox.
10277  * @param {Object} config Configuration options
10278  */
10279 Roo.bootstrap.ComboBox = function(config){
10280     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10281     this.addEvents({
10282         /**
10283          * @event expand
10284          * Fires when the dropdown list is expanded
10285              * @param {Roo.bootstrap.ComboBox} combo This combo box
10286              */
10287         'expand' : true,
10288         /**
10289          * @event collapse
10290          * Fires when the dropdown list is collapsed
10291              * @param {Roo.bootstrap.ComboBox} combo This combo box
10292              */
10293         'collapse' : true,
10294         /**
10295          * @event beforeselect
10296          * Fires before a list item is selected. Return false to cancel the selection.
10297              * @param {Roo.bootstrap.ComboBox} combo This combo box
10298              * @param {Roo.data.Record} record The data record returned from the underlying store
10299              * @param {Number} index The index of the selected item in the dropdown list
10300              */
10301         'beforeselect' : true,
10302         /**
10303          * @event select
10304          * Fires when a list item is selected
10305              * @param {Roo.bootstrap.ComboBox} combo This combo box
10306              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10307              * @param {Number} index The index of the selected item in the dropdown list
10308              */
10309         'select' : true,
10310         /**
10311          * @event beforequery
10312          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10313          * The event object passed has these properties:
10314              * @param {Roo.bootstrap.ComboBox} combo This combo box
10315              * @param {String} query The query
10316              * @param {Boolean} forceAll true to force "all" query
10317              * @param {Boolean} cancel true to cancel the query
10318              * @param {Object} e The query event object
10319              */
10320         'beforequery': true,
10321          /**
10322          * @event add
10323          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10324              * @param {Roo.bootstrap.ComboBox} combo This combo box
10325              */
10326         'add' : true,
10327         /**
10328          * @event edit
10329          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10330              * @param {Roo.bootstrap.ComboBox} combo This combo box
10331              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10332              */
10333         'edit' : true,
10334         /**
10335          * @event remove
10336          * Fires when the remove value from the combobox array
10337              * @param {Roo.bootstrap.ComboBox} combo This combo box
10338              */
10339         'remove' : true
10340         
10341     });
10342     
10343     this.item = [];
10344     this.tickItems = [];
10345     
10346     this.selectedIndex = -1;
10347     if(this.mode == 'local'){
10348         if(config.queryDelay === undefined){
10349             this.queryDelay = 10;
10350         }
10351         if(config.minChars === undefined){
10352             this.minChars = 0;
10353         }
10354     }
10355 };
10356
10357 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10358      
10359     /**
10360      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10361      * rendering into an Roo.Editor, defaults to false)
10362      */
10363     /**
10364      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10365      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10366      */
10367     /**
10368      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10369      */
10370     /**
10371      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10372      * the dropdown list (defaults to undefined, with no header element)
10373      */
10374
10375      /**
10376      * @cfg {String/Roo.Template} tpl The template to use to render the output
10377      */
10378      
10379      /**
10380      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10381      */
10382     listWidth: undefined,
10383     /**
10384      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10385      * mode = 'remote' or 'text' if mode = 'local')
10386      */
10387     displayField: undefined,
10388     /**
10389      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10390      * mode = 'remote' or 'value' if mode = 'local'). 
10391      * Note: use of a valueField requires the user make a selection
10392      * in order for a value to be mapped.
10393      */
10394     valueField: undefined,
10395     
10396     
10397     /**
10398      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10399      * field's data value (defaults to the underlying DOM element's name)
10400      */
10401     hiddenName: undefined,
10402     /**
10403      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10404      */
10405     listClass: '',
10406     /**
10407      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10408      */
10409     selectedClass: 'active',
10410     
10411     /**
10412      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10413      */
10414     shadow:'sides',
10415     /**
10416      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10417      * anchor positions (defaults to 'tl-bl')
10418      */
10419     listAlign: 'tl-bl?',
10420     /**
10421      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10422      */
10423     maxHeight: 300,
10424     /**
10425      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10426      * query specified by the allQuery config option (defaults to 'query')
10427      */
10428     triggerAction: 'query',
10429     /**
10430      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10431      * (defaults to 4, does not apply if editable = false)
10432      */
10433     minChars : 4,
10434     /**
10435      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10436      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10437      */
10438     typeAhead: false,
10439     /**
10440      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10441      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10442      */
10443     queryDelay: 500,
10444     /**
10445      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10446      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10447      */
10448     pageSize: 0,
10449     /**
10450      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10451      * when editable = true (defaults to false)
10452      */
10453     selectOnFocus:false,
10454     /**
10455      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10456      */
10457     queryParam: 'query',
10458     /**
10459      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10460      * when mode = 'remote' (defaults to 'Loading...')
10461      */
10462     loadingText: 'Loading...',
10463     /**
10464      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10465      */
10466     resizable: false,
10467     /**
10468      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10469      */
10470     handleHeight : 8,
10471     /**
10472      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10473      * traditional select (defaults to true)
10474      */
10475     editable: true,
10476     /**
10477      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10478      */
10479     allQuery: '',
10480     /**
10481      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10482      */
10483     mode: 'remote',
10484     /**
10485      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10486      * listWidth has a higher value)
10487      */
10488     minListWidth : 70,
10489     /**
10490      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10491      * allow the user to set arbitrary text into the field (defaults to false)
10492      */
10493     forceSelection:false,
10494     /**
10495      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10496      * if typeAhead = true (defaults to 250)
10497      */
10498     typeAheadDelay : 250,
10499     /**
10500      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10501      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10502      */
10503     valueNotFoundText : undefined,
10504     /**
10505      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10506      */
10507     blockFocus : false,
10508     
10509     /**
10510      * @cfg {Boolean} disableClear Disable showing of clear button.
10511      */
10512     disableClear : false,
10513     /**
10514      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10515      */
10516     alwaysQuery : false,
10517     
10518     /**
10519      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10520      */
10521     multiple : false,
10522     
10523     //private
10524     addicon : false,
10525     editicon: false,
10526     
10527     page: 0,
10528     hasQuery: false,
10529     append: false,
10530     loadNext: false,
10531     autoFocus : true,
10532     tickable : false,
10533     btnPosition : 'right',
10534     triggerList : true,
10535     showToggleBtn : true,
10536     // element that contains real text value.. (when hidden is used..)
10537     
10538     getAutoCreate : function()
10539     {
10540         var cfg = false;
10541         
10542         /*
10543          *  Normal ComboBox
10544          */
10545         if(!this.tickable){
10546             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10547             return cfg;
10548         }
10549         
10550         /*
10551          *  ComboBox with tickable selections
10552          */
10553              
10554         var align = this.labelAlign || this.parentLabelAlign();
10555         
10556         cfg = {
10557             cls : 'form-group roo-combobox-tickable' //input-group
10558         };
10559         
10560         
10561         var buttons = {
10562             tag : 'div',
10563             cls : 'tickable-buttons',
10564             cn : [
10565                 {
10566                     tag : 'button',
10567                     type : 'button',
10568                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10569                     html : 'Edit'
10570                 },
10571                 {
10572                     tag : 'button',
10573                     type : 'button',
10574                     name : 'ok',
10575                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10576                     html : 'Done'
10577                 },
10578                 {
10579                     tag : 'button',
10580                     type : 'button',
10581                     name : 'cancel',
10582                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10583                     html : 'Cancel'
10584                 }
10585             ]
10586         };
10587         
10588         var _this = this;
10589         Roo.each(buttons.cn, function(c){
10590             if (_this.size) {
10591                 c.cls += ' btn-' + _this.size;
10592             }
10593
10594             if (_this.disabled) {
10595                 c.disabled = true;
10596             }
10597         });
10598         
10599         var box = {
10600             tag: 'div',
10601             cn: [
10602                 {
10603                     tag: 'input',
10604                     type : 'hidden',
10605                     cls: 'form-hidden-field'
10606                 },
10607                 {
10608                     tag: 'ul',
10609                     cls: 'select2-choices',
10610                     cn:[
10611                         {
10612                             tag: 'li',
10613                             cls: 'select2-search-field',
10614                             cn: [
10615
10616                                 buttons
10617                             ]
10618                         }
10619                     ]
10620                 }
10621             ]
10622         }
10623         
10624         var combobox = {
10625             cls: 'select2-container input-group select2-container-multi',
10626             cn: [
10627                 box
10628 //                {
10629 //                    tag: 'ul',
10630 //                    cls: 'typeahead typeahead-long dropdown-menu',
10631 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10632 //                }
10633             ]
10634         };
10635         
10636         if (align ==='left' && this.fieldLabel.length) {
10637             
10638                 Roo.log("left and has label");
10639                 cfg.cn = [
10640                     
10641                     {
10642                         tag: 'label',
10643                         'for' :  id,
10644                         cls : 'control-label col-sm-' + this.labelWidth,
10645                         html : this.fieldLabel
10646                         
10647                     },
10648                     {
10649                         cls : "col-sm-" + (12 - this.labelWidth), 
10650                         cn: [
10651                             combobox
10652                         ]
10653                     }
10654                     
10655                 ];
10656         } else if ( this.fieldLabel.length) {
10657                 Roo.log(" label");
10658                  cfg.cn = [
10659                    
10660                     {
10661                         tag: 'label',
10662                         //cls : 'input-group-addon',
10663                         html : this.fieldLabel
10664                         
10665                     },
10666                     
10667                     combobox
10668                     
10669                 ];
10670
10671         } else {
10672             
10673                 Roo.log(" no label && no align");
10674                 cfg = combobox
10675                      
10676                 
10677         }
10678          
10679         var settings=this;
10680         ['xs','sm','md','lg'].map(function(size){
10681             if (settings[size]) {
10682                 cfg.cls += ' col-' + size + '-' + settings[size];
10683             }
10684         });
10685         
10686         return cfg;
10687         
10688     },
10689     
10690     // private
10691     initEvents: function()
10692     {
10693         
10694         if (!this.store) {
10695             throw "can not find store for combo";
10696         }
10697         this.store = Roo.factory(this.store, Roo.data);
10698         
10699         if(this.tickable){
10700             this.initTickableEvents();
10701             return;
10702         }
10703         
10704         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10705         
10706         if(this.hiddenName){
10707             
10708             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10709             
10710             this.hiddenField.dom.value =
10711                 this.hiddenValue !== undefined ? this.hiddenValue :
10712                 this.value !== undefined ? this.value : '';
10713
10714             // prevent input submission
10715             this.el.dom.removeAttribute('name');
10716             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10717              
10718              
10719         }
10720         //if(Roo.isGecko){
10721         //    this.el.dom.setAttribute('autocomplete', 'off');
10722         //}
10723         
10724         var cls = 'x-combo-list';
10725         
10726         //this.list = new Roo.Layer({
10727         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10728         //});
10729         
10730         var _this = this;
10731         
10732         (function(){
10733             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10734             _this.list.setWidth(lw);
10735         }).defer(100);
10736         
10737         this.list.on('mouseover', this.onViewOver, this);
10738         this.list.on('mousemove', this.onViewMove, this);
10739         
10740         this.list.on('scroll', this.onViewScroll, this);
10741         
10742         /*
10743         this.list.swallowEvent('mousewheel');
10744         this.assetHeight = 0;
10745
10746         if(this.title){
10747             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10748             this.assetHeight += this.header.getHeight();
10749         }
10750
10751         this.innerList = this.list.createChild({cls:cls+'-inner'});
10752         this.innerList.on('mouseover', this.onViewOver, this);
10753         this.innerList.on('mousemove', this.onViewMove, this);
10754         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10755         
10756         if(this.allowBlank && !this.pageSize && !this.disableClear){
10757             this.footer = this.list.createChild({cls:cls+'-ft'});
10758             this.pageTb = new Roo.Toolbar(this.footer);
10759            
10760         }
10761         if(this.pageSize){
10762             this.footer = this.list.createChild({cls:cls+'-ft'});
10763             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10764                     {pageSize: this.pageSize});
10765             
10766         }
10767         
10768         if (this.pageTb && this.allowBlank && !this.disableClear) {
10769             var _this = this;
10770             this.pageTb.add(new Roo.Toolbar.Fill(), {
10771                 cls: 'x-btn-icon x-btn-clear',
10772                 text: '&#160;',
10773                 handler: function()
10774                 {
10775                     _this.collapse();
10776                     _this.clearValue();
10777                     _this.onSelect(false, -1);
10778                 }
10779             });
10780         }
10781         if (this.footer) {
10782             this.assetHeight += this.footer.getHeight();
10783         }
10784         */
10785             
10786         if(!this.tpl){
10787             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10788         }
10789
10790         this.view = new Roo.View(this.list, this.tpl, {
10791             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10792         });
10793         //this.view.wrapEl.setDisplayed(false);
10794         this.view.on('click', this.onViewClick, this);
10795         
10796         
10797         
10798         this.store.on('beforeload', this.onBeforeLoad, this);
10799         this.store.on('load', this.onLoad, this);
10800         this.store.on('loadexception', this.onLoadException, this);
10801         /*
10802         if(this.resizable){
10803             this.resizer = new Roo.Resizable(this.list,  {
10804                pinned:true, handles:'se'
10805             });
10806             this.resizer.on('resize', function(r, w, h){
10807                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10808                 this.listWidth = w;
10809                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10810                 this.restrictHeight();
10811             }, this);
10812             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10813         }
10814         */
10815         if(!this.editable){
10816             this.editable = true;
10817             this.setEditable(false);
10818         }
10819         
10820         /*
10821         
10822         if (typeof(this.events.add.listeners) != 'undefined') {
10823             
10824             this.addicon = this.wrap.createChild(
10825                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10826        
10827             this.addicon.on('click', function(e) {
10828                 this.fireEvent('add', this);
10829             }, this);
10830         }
10831         if (typeof(this.events.edit.listeners) != 'undefined') {
10832             
10833             this.editicon = this.wrap.createChild(
10834                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10835             if (this.addicon) {
10836                 this.editicon.setStyle('margin-left', '40px');
10837             }
10838             this.editicon.on('click', function(e) {
10839                 
10840                 // we fire even  if inothing is selected..
10841                 this.fireEvent('edit', this, this.lastData );
10842                 
10843             }, this);
10844         }
10845         */
10846         
10847         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10848             "up" : function(e){
10849                 this.inKeyMode = true;
10850                 this.selectPrev();
10851             },
10852
10853             "down" : function(e){
10854                 if(!this.isExpanded()){
10855                     this.onTriggerClick();
10856                 }else{
10857                     this.inKeyMode = true;
10858                     this.selectNext();
10859                 }
10860             },
10861
10862             "enter" : function(e){
10863 //                this.onViewClick();
10864                 //return true;
10865                 this.collapse();
10866                 
10867                 if(this.fireEvent("specialkey", this, e)){
10868                     this.onViewClick(false);
10869                 }
10870                 
10871                 return true;
10872             },
10873
10874             "esc" : function(e){
10875                 this.collapse();
10876             },
10877
10878             "tab" : function(e){
10879                 this.collapse();
10880                 
10881                 if(this.fireEvent("specialkey", this, e)){
10882                     this.onViewClick(false);
10883                 }
10884                 
10885                 return true;
10886             },
10887
10888             scope : this,
10889
10890             doRelay : function(foo, bar, hname){
10891                 if(hname == 'down' || this.scope.isExpanded()){
10892                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10893                 }
10894                 return true;
10895             },
10896
10897             forceKeyDown: true
10898         });
10899         
10900         
10901         this.queryDelay = Math.max(this.queryDelay || 10,
10902                 this.mode == 'local' ? 10 : 250);
10903         
10904         
10905         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10906         
10907         if(this.typeAhead){
10908             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10909         }
10910         if(this.editable !== false){
10911             this.inputEl().on("keyup", this.onKeyUp, this);
10912         }
10913         if(this.forceSelection){
10914             this.inputEl().on('blur', this.doForce, this);
10915         }
10916         
10917         if(this.multiple){
10918             this.choices = this.el.select('ul.select2-choices', true).first();
10919             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10920         }
10921     },
10922     
10923     initTickableEvents: function()
10924     {   
10925         this.createList();
10926         
10927         if(this.hiddenName){
10928             
10929             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10930             
10931             this.hiddenField.dom.value =
10932                 this.hiddenValue !== undefined ? this.hiddenValue :
10933                 this.value !== undefined ? this.value : '';
10934
10935             // prevent input submission
10936             this.el.dom.removeAttribute('name');
10937             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10938              
10939              
10940         }
10941         
10942 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10943         
10944         this.choices = this.el.select('ul.select2-choices', true).first();
10945         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10946         if(this.triggerList){
10947             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10948         }
10949          
10950         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10951         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10952         
10953         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10954         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10955         
10956         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10957         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10958         
10959         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10960         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10961         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10962         
10963         this.okBtn.hide();
10964         this.cancelBtn.hide();
10965         
10966         var _this = this;
10967         
10968         (function(){
10969             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10970             _this.list.setWidth(lw);
10971         }).defer(100);
10972         
10973         this.list.on('mouseover', this.onViewOver, this);
10974         this.list.on('mousemove', this.onViewMove, this);
10975         
10976         this.list.on('scroll', this.onViewScroll, this);
10977         
10978         if(!this.tpl){
10979             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>';
10980         }
10981
10982         this.view = new Roo.View(this.list, this.tpl, {
10983             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10984         });
10985         
10986         //this.view.wrapEl.setDisplayed(false);
10987         this.view.on('click', this.onViewClick, this);
10988         
10989         
10990         
10991         this.store.on('beforeload', this.onBeforeLoad, this);
10992         this.store.on('load', this.onLoad, this);
10993         this.store.on('loadexception', this.onLoadException, this);
10994         
10995 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10996 //            "up" : function(e){
10997 //                this.inKeyMode = true;
10998 //                this.selectPrev();
10999 //            },
11000 //
11001 //            "down" : function(e){
11002 //                if(!this.isExpanded()){
11003 //                    this.onTriggerClick();
11004 //                }else{
11005 //                    this.inKeyMode = true;
11006 //                    this.selectNext();
11007 //                }
11008 //            },
11009 //
11010 //            "enter" : function(e){
11011 ////                this.onViewClick();
11012 //                //return true;
11013 //                this.collapse();
11014 //                
11015 //                if(this.fireEvent("specialkey", this, e)){
11016 //                    this.onViewClick(false);
11017 //                }
11018 //                
11019 //                return true;
11020 //            },
11021 //
11022 //            "esc" : function(e){
11023 //                this.collapse();
11024 //            },
11025 //
11026 //            "tab" : function(e){
11027 //                this.collapse();
11028 //                
11029 //                if(this.fireEvent("specialkey", this, e)){
11030 //                    this.onViewClick(false);
11031 //                }
11032 //                
11033 //                return true;
11034 //            },
11035 //
11036 //            scope : this,
11037 //
11038 //            doRelay : function(foo, bar, hname){
11039 //                if(hname == 'down' || this.scope.isExpanded()){
11040 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11041 //                }
11042 //                return true;
11043 //            },
11044 //
11045 //            forceKeyDown: true
11046 //        });
11047         
11048         
11049         this.queryDelay = Math.max(this.queryDelay || 10,
11050                 this.mode == 'local' ? 10 : 250);
11051         
11052         
11053         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11054         
11055         if(this.typeAhead){
11056             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11057         }
11058     },
11059
11060     onDestroy : function(){
11061         if(this.view){
11062             this.view.setStore(null);
11063             this.view.el.removeAllListeners();
11064             this.view.el.remove();
11065             this.view.purgeListeners();
11066         }
11067         if(this.list){
11068             this.list.dom.innerHTML  = '';
11069         }
11070         
11071         if(this.store){
11072             this.store.un('beforeload', this.onBeforeLoad, this);
11073             this.store.un('load', this.onLoad, this);
11074             this.store.un('loadexception', this.onLoadException, this);
11075         }
11076         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11077     },
11078
11079     // private
11080     fireKey : function(e){
11081         if(e.isNavKeyPress() && !this.list.isVisible()){
11082             this.fireEvent("specialkey", this, e);
11083         }
11084     },
11085
11086     // private
11087     onResize: function(w, h){
11088 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11089 //        
11090 //        if(typeof w != 'number'){
11091 //            // we do not handle it!?!?
11092 //            return;
11093 //        }
11094 //        var tw = this.trigger.getWidth();
11095 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11096 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11097 //        var x = w - tw;
11098 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11099 //            
11100 //        //this.trigger.setStyle('left', x+'px');
11101 //        
11102 //        if(this.list && this.listWidth === undefined){
11103 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11104 //            this.list.setWidth(lw);
11105 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11106 //        }
11107         
11108     
11109         
11110     },
11111
11112     /**
11113      * Allow or prevent the user from directly editing the field text.  If false is passed,
11114      * the user will only be able to select from the items defined in the dropdown list.  This method
11115      * is the runtime equivalent of setting the 'editable' config option at config time.
11116      * @param {Boolean} value True to allow the user to directly edit the field text
11117      */
11118     setEditable : function(value){
11119         if(value == this.editable){
11120             return;
11121         }
11122         this.editable = value;
11123         if(!value){
11124             this.inputEl().dom.setAttribute('readOnly', true);
11125             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11126             this.inputEl().addClass('x-combo-noedit');
11127         }else{
11128             this.inputEl().dom.setAttribute('readOnly', false);
11129             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11130             this.inputEl().removeClass('x-combo-noedit');
11131         }
11132     },
11133
11134     // private
11135     
11136     onBeforeLoad : function(combo,opts){
11137         if(!this.hasFocus){
11138             return;
11139         }
11140          if (!opts.add) {
11141             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11142          }
11143         this.restrictHeight();
11144         this.selectedIndex = -1;
11145     },
11146
11147     // private
11148     onLoad : function(){
11149         
11150         this.hasQuery = false;
11151         
11152         if(!this.hasFocus){
11153             return;
11154         }
11155         
11156         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11157             this.loading.hide();
11158         }
11159         
11160         if(this.store.getCount() > 0){
11161             this.expand();
11162 //            this.restrictHeight();
11163             if(this.lastQuery == this.allQuery){
11164                 if(this.editable && !this.tickable){
11165                     this.inputEl().dom.select();
11166                 }
11167                 
11168                 if(
11169                     !this.selectByValue(this.value, true) &&
11170                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11171                     this.store.lastOptions.add != true)
11172                 ){
11173                     this.select(0, true);
11174                 }
11175             }else{
11176                 if(this.autoFocus){
11177                     this.selectNext();
11178                 }
11179                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11180                     this.taTask.delay(this.typeAheadDelay);
11181                 }
11182             }
11183         }else{
11184             this.onEmptyResults();
11185         }
11186         
11187         //this.el.focus();
11188     },
11189     // private
11190     onLoadException : function()
11191     {
11192         this.hasQuery = false;
11193         
11194         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11195             this.loading.hide();
11196         }
11197         
11198         this.collapse();
11199         Roo.log(this.store.reader.jsonData);
11200         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11201             // fixme
11202             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11203         }
11204         
11205         
11206     },
11207     // private
11208     onTypeAhead : function(){
11209         if(this.store.getCount() > 0){
11210             var r = this.store.getAt(0);
11211             var newValue = r.data[this.displayField];
11212             var len = newValue.length;
11213             var selStart = this.getRawValue().length;
11214             
11215             if(selStart != len){
11216                 this.setRawValue(newValue);
11217                 this.selectText(selStart, newValue.length);
11218             }
11219         }
11220     },
11221
11222     // private
11223     onSelect : function(record, index){
11224         
11225         if(this.fireEvent('beforeselect', this, record, index) !== false){
11226         
11227             this.setFromData(index > -1 ? record.data : false);
11228             
11229             this.collapse();
11230             this.fireEvent('select', this, record, index);
11231         }
11232     },
11233
11234     /**
11235      * Returns the currently selected field value or empty string if no value is set.
11236      * @return {String} value The selected value
11237      */
11238     getValue : function(){
11239         
11240         if(this.multiple){
11241             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11242         }
11243         
11244         if(this.valueField){
11245             return typeof this.value != 'undefined' ? this.value : '';
11246         }else{
11247             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11248         }
11249     },
11250
11251     /**
11252      * Clears any text/value currently set in the field
11253      */
11254     clearValue : function(){
11255         if(this.hiddenField){
11256             this.hiddenField.dom.value = '';
11257         }
11258         this.value = '';
11259         this.setRawValue('');
11260         this.lastSelectionText = '';
11261         
11262     },
11263
11264     /**
11265      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11266      * will be displayed in the field.  If the value does not match the data value of an existing item,
11267      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11268      * Otherwise the field will be blank (although the value will still be set).
11269      * @param {String} value The value to match
11270      */
11271     setValue : function(v){
11272         if(this.multiple){
11273             this.syncValue();
11274             return;
11275         }
11276         
11277         var text = v;
11278         if(this.valueField){
11279             var r = this.findRecord(this.valueField, v);
11280             if(r){
11281                 text = r.data[this.displayField];
11282             }else if(this.valueNotFoundText !== undefined){
11283                 text = this.valueNotFoundText;
11284             }
11285         }
11286         this.lastSelectionText = text;
11287         if(this.hiddenField){
11288             this.hiddenField.dom.value = v;
11289         }
11290         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11291         this.value = v;
11292     },
11293     /**
11294      * @property {Object} the last set data for the element
11295      */
11296     
11297     lastData : false,
11298     /**
11299      * Sets the value of the field based on a object which is related to the record format for the store.
11300      * @param {Object} value the value to set as. or false on reset?
11301      */
11302     setFromData : function(o){
11303         
11304         if(this.multiple){
11305             if(typeof o.display_name !== 'string'){
11306                 for(var i=0;i<o.display_name.length;i++){
11307                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11308                 }
11309                 return;
11310             }
11311             this.addItem(o);
11312             return;
11313         }
11314             
11315         var dv = ''; // display value
11316         var vv = ''; // value value..
11317         this.lastData = o;
11318         if (this.displayField) {
11319             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11320         } else {
11321             // this is an error condition!!!
11322             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11323         }
11324         
11325         if(this.valueField){
11326             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11327         }
11328         
11329         if(this.hiddenField){
11330             this.hiddenField.dom.value = vv;
11331             
11332             this.lastSelectionText = dv;
11333             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11334             this.value = vv;
11335             return;
11336         }
11337         // no hidden field.. - we store the value in 'value', but still display
11338         // display field!!!!
11339         this.lastSelectionText = dv;
11340         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11341         this.value = vv;
11342         
11343         
11344     },
11345     // private
11346     reset : function(){
11347         // overridden so that last data is reset..
11348         this.setValue(this.originalValue);
11349         this.clearInvalid();
11350         this.lastData = false;
11351         if (this.view) {
11352             this.view.clearSelections();
11353         }
11354     },
11355     // private
11356     findRecord : function(prop, value){
11357         var record;
11358         if(this.store.getCount() > 0){
11359             this.store.each(function(r){
11360                 if(r.data[prop] == value){
11361                     record = r;
11362                     return false;
11363                 }
11364                 return true;
11365             });
11366         }
11367         return record;
11368     },
11369     
11370     getName: function()
11371     {
11372         // returns hidden if it's set..
11373         if (!this.rendered) {return ''};
11374         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11375         
11376     },
11377     // private
11378     onViewMove : function(e, t){
11379         this.inKeyMode = false;
11380     },
11381
11382     // private
11383     onViewOver : function(e, t){
11384         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11385             return;
11386         }
11387         var item = this.view.findItemFromChild(t);
11388         
11389         if(item){
11390             var index = this.view.indexOf(item);
11391             this.select(index, false);
11392         }
11393     },
11394
11395     // private
11396     onViewClick : function(view, doFocus, el, e)
11397     {
11398         var index = this.view.getSelectedIndexes()[0];
11399         
11400         var r = this.store.getAt(index);
11401         
11402         if(this.tickable){
11403             
11404             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11405                 return;
11406             }
11407             
11408             var rm = false;
11409             var _this = this;
11410             
11411             Roo.each(this.tickItems, function(v,k){
11412                 
11413                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11414                     _this.tickItems.splice(k, 1);
11415                     rm = true;
11416                     return;
11417                 }
11418             })
11419             
11420             if(rm){
11421                 return;
11422             }
11423             
11424             this.tickItems.push(r.data);
11425             return;
11426         }
11427         
11428         if(r){
11429             this.onSelect(r, index);
11430         }
11431         if(doFocus !== false && !this.blockFocus){
11432             this.inputEl().focus();
11433         }
11434     },
11435
11436     // private
11437     restrictHeight : function(){
11438         //this.innerList.dom.style.height = '';
11439         //var inner = this.innerList.dom;
11440         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11441         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11442         //this.list.beginUpdate();
11443         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11444         this.list.alignTo(this.inputEl(), this.listAlign);
11445         this.list.alignTo(this.inputEl(), this.listAlign);
11446         //this.list.endUpdate();
11447     },
11448
11449     // private
11450     onEmptyResults : function(){
11451         this.collapse();
11452     },
11453
11454     /**
11455      * Returns true if the dropdown list is expanded, else false.
11456      */
11457     isExpanded : function(){
11458         return this.list.isVisible();
11459     },
11460
11461     /**
11462      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11463      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11464      * @param {String} value The data value of the item to select
11465      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11466      * selected item if it is not currently in view (defaults to true)
11467      * @return {Boolean} True if the value matched an item in the list, else false
11468      */
11469     selectByValue : function(v, scrollIntoView){
11470         if(v !== undefined && v !== null){
11471             var r = this.findRecord(this.valueField || this.displayField, v);
11472             if(r){
11473                 this.select(this.store.indexOf(r), scrollIntoView);
11474                 return true;
11475             }
11476         }
11477         return false;
11478     },
11479
11480     /**
11481      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11482      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11483      * @param {Number} index The zero-based index of the list item to select
11484      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11485      * selected item if it is not currently in view (defaults to true)
11486      */
11487     select : function(index, scrollIntoView){
11488         this.selectedIndex = index;
11489         this.view.select(index);
11490         if(scrollIntoView !== false){
11491             var el = this.view.getNode(index);
11492             if(el && !this.multiple && !this.tickable){
11493                 this.list.scrollChildIntoView(el, false);
11494             }
11495         }
11496     },
11497
11498     // private
11499     selectNext : function(){
11500         var ct = this.store.getCount();
11501         if(ct > 0){
11502             if(this.selectedIndex == -1){
11503                 this.select(0);
11504             }else if(this.selectedIndex < ct-1){
11505                 this.select(this.selectedIndex+1);
11506             }
11507         }
11508     },
11509
11510     // private
11511     selectPrev : function(){
11512         var ct = this.store.getCount();
11513         if(ct > 0){
11514             if(this.selectedIndex == -1){
11515                 this.select(0);
11516             }else if(this.selectedIndex != 0){
11517                 this.select(this.selectedIndex-1);
11518             }
11519         }
11520     },
11521
11522     // private
11523     onKeyUp : function(e){
11524         if(this.editable !== false && !e.isSpecialKey()){
11525             this.lastKey = e.getKey();
11526             this.dqTask.delay(this.queryDelay);
11527         }
11528     },
11529
11530     // private
11531     validateBlur : function(){
11532         return !this.list || !this.list.isVisible();   
11533     },
11534
11535     // private
11536     initQuery : function(){
11537         this.doQuery(this.getRawValue());
11538     },
11539
11540     // private
11541     doForce : function(){
11542         if(this.inputEl().dom.value.length > 0){
11543             this.inputEl().dom.value =
11544                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11545              
11546         }
11547     },
11548
11549     /**
11550      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11551      * query allowing the query action to be canceled if needed.
11552      * @param {String} query The SQL query to execute
11553      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11554      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11555      * saved in the current store (defaults to false)
11556      */
11557     doQuery : function(q, forceAll){
11558         
11559         if(q === undefined || q === null){
11560             q = '';
11561         }
11562         var qe = {
11563             query: q,
11564             forceAll: forceAll,
11565             combo: this,
11566             cancel:false
11567         };
11568         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11569             return false;
11570         }
11571         q = qe.query;
11572         
11573         forceAll = qe.forceAll;
11574         if(forceAll === true || (q.length >= this.minChars)){
11575             
11576             this.hasQuery = true;
11577             
11578             if(this.lastQuery != q || this.alwaysQuery){
11579                 this.lastQuery = q;
11580                 if(this.mode == 'local'){
11581                     this.selectedIndex = -1;
11582                     if(forceAll){
11583                         this.store.clearFilter();
11584                     }else{
11585                         this.store.filter(this.displayField, q);
11586                     }
11587                     this.onLoad();
11588                 }else{
11589                     this.store.baseParams[this.queryParam] = q;
11590                     
11591                     var options = {params : this.getParams(q)};
11592                     
11593                     if(this.loadNext){
11594                         options.add = true;
11595                         options.params.start = this.page * this.pageSize;
11596                     }
11597                     
11598                     this.store.load(options);
11599                     /*
11600                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11601                      *  we should expand the list on onLoad
11602                      *  so command out it
11603                      */
11604 //                    this.expand();
11605                 }
11606             }else{
11607                 this.selectedIndex = -1;
11608                 this.onLoad();   
11609             }
11610         }
11611         
11612         this.loadNext = false;
11613     },
11614
11615     // private
11616     getParams : function(q){
11617         var p = {};
11618         //p[this.queryParam] = q;
11619         
11620         if(this.pageSize){
11621             p.start = 0;
11622             p.limit = this.pageSize;
11623         }
11624         return p;
11625     },
11626
11627     /**
11628      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11629      */
11630     collapse : function(){
11631         if(!this.isExpanded()){
11632             return;
11633         }
11634         
11635         this.list.hide();
11636         
11637         if(this.tickable){
11638             this.okBtn.hide();
11639             this.cancelBtn.hide();
11640             this.trigger.show();
11641         }
11642         
11643         Roo.get(document).un('mousedown', this.collapseIf, this);
11644         Roo.get(document).un('mousewheel', this.collapseIf, this);
11645         if (!this.editable) {
11646             Roo.get(document).un('keydown', this.listKeyPress, this);
11647         }
11648         this.fireEvent('collapse', this);
11649     },
11650
11651     // private
11652     collapseIf : function(e){
11653         var in_combo  = e.within(this.el);
11654         var in_list =  e.within(this.list);
11655         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11656         
11657         if (in_combo || in_list || is_list) {
11658             //e.stopPropagation();
11659             return;
11660         }
11661         
11662         if(this.tickable){
11663             this.onTickableFooterButtonClick(e, false, false);
11664         }
11665
11666         this.collapse();
11667         
11668     },
11669
11670     /**
11671      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11672      */
11673     expand : function(){
11674        
11675         if(this.isExpanded() || !this.hasFocus){
11676             return;
11677         }
11678         
11679         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11680         this.list.setWidth(lw);
11681         
11682         
11683          Roo.log('expand');
11684         
11685         this.list.show();
11686         
11687         this.restrictHeight();
11688         
11689         if(this.tickable){
11690             
11691             this.tickItems = Roo.apply([], this.item);
11692             
11693             this.okBtn.show();
11694             this.cancelBtn.show();
11695             this.trigger.hide();
11696             
11697         }
11698         
11699         Roo.get(document).on('mousedown', this.collapseIf, this);
11700         Roo.get(document).on('mousewheel', this.collapseIf, this);
11701         if (!this.editable) {
11702             Roo.get(document).on('keydown', this.listKeyPress, this);
11703         }
11704         
11705         this.fireEvent('expand', this);
11706     },
11707
11708     // private
11709     // Implements the default empty TriggerField.onTriggerClick function
11710     onTriggerClick : function(e)
11711     {
11712         Roo.log('trigger click');
11713         
11714         if(this.disabled || !this.triggerList){
11715             return;
11716         }
11717         
11718         this.page = 0;
11719         this.loadNext = false;
11720         
11721         if(this.isExpanded()){
11722             this.collapse();
11723             if (!this.blockFocus) {
11724                 this.inputEl().focus();
11725             }
11726             
11727         }else {
11728             this.hasFocus = true;
11729             if(this.triggerAction == 'all') {
11730                 this.doQuery(this.allQuery, true);
11731             } else {
11732                 this.doQuery(this.getRawValue());
11733             }
11734             if (!this.blockFocus) {
11735                 this.inputEl().focus();
11736             }
11737         }
11738     },
11739     
11740     onTickableTriggerClick : function(e)
11741     {
11742         if(this.disabled){
11743             return;
11744         }
11745         
11746         this.page = 0;
11747         this.loadNext = false;
11748         this.hasFocus = true;
11749         
11750         if(this.triggerAction == 'all') {
11751             this.doQuery(this.allQuery, true);
11752         } else {
11753             this.doQuery(this.getRawValue());
11754         }
11755     },
11756     
11757     onSearchFieldClick : function(e)
11758     {
11759         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11760             return;
11761         }
11762         
11763         this.page = 0;
11764         this.loadNext = false;
11765         this.hasFocus = true;
11766         
11767         if(this.triggerAction == 'all') {
11768             this.doQuery(this.allQuery, true);
11769         } else {
11770             this.doQuery(this.getRawValue());
11771         }
11772     },
11773     
11774     listKeyPress : function(e)
11775     {
11776         //Roo.log('listkeypress');
11777         // scroll to first matching element based on key pres..
11778         if (e.isSpecialKey()) {
11779             return false;
11780         }
11781         var k = String.fromCharCode(e.getKey()).toUpperCase();
11782         //Roo.log(k);
11783         var match  = false;
11784         var csel = this.view.getSelectedNodes();
11785         var cselitem = false;
11786         if (csel.length) {
11787             var ix = this.view.indexOf(csel[0]);
11788             cselitem  = this.store.getAt(ix);
11789             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11790                 cselitem = false;
11791             }
11792             
11793         }
11794         
11795         this.store.each(function(v) { 
11796             if (cselitem) {
11797                 // start at existing selection.
11798                 if (cselitem.id == v.id) {
11799                     cselitem = false;
11800                 }
11801                 return true;
11802             }
11803                 
11804             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11805                 match = this.store.indexOf(v);
11806                 return false;
11807             }
11808             return true;
11809         }, this);
11810         
11811         if (match === false) {
11812             return true; // no more action?
11813         }
11814         // scroll to?
11815         this.view.select(match);
11816         var sn = Roo.get(this.view.getSelectedNodes()[0])
11817         //sn.scrollIntoView(sn.dom.parentNode, false);
11818     },
11819     
11820     onViewScroll : function(e, t){
11821         
11822         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){
11823             return;
11824         }
11825         
11826         this.hasQuery = true;
11827         
11828         this.loading = this.list.select('.loading', true).first();
11829         
11830         if(this.loading === null){
11831             this.list.createChild({
11832                 tag: 'div',
11833                 cls: 'loading select2-more-results select2-active',
11834                 html: 'Loading more results...'
11835             })
11836             
11837             this.loading = this.list.select('.loading', true).first();
11838             
11839             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11840             
11841             this.loading.hide();
11842         }
11843         
11844         this.loading.show();
11845         
11846         var _combo = this;
11847         
11848         this.page++;
11849         this.loadNext = true;
11850         
11851         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11852         
11853         return;
11854     },
11855     
11856     addItem : function(o)
11857     {   
11858         var dv = ''; // display value
11859         
11860         if (this.displayField) {
11861             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11862         } else {
11863             // this is an error condition!!!
11864             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11865         }
11866         
11867         if(!dv.length){
11868             return;
11869         }
11870         
11871         var choice = this.choices.createChild({
11872             tag: 'li',
11873             cls: 'select2-search-choice',
11874             cn: [
11875                 {
11876                     tag: 'div',
11877                     html: dv
11878                 },
11879                 {
11880                     tag: 'a',
11881                     href: '#',
11882                     cls: 'select2-search-choice-close',
11883                     tabindex: '-1'
11884                 }
11885             ]
11886             
11887         }, this.searchField);
11888         
11889         var close = choice.select('a.select2-search-choice-close', true).first()
11890         
11891         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11892         
11893         this.item.push(o);
11894         
11895         this.lastData = o;
11896         
11897         this.syncValue();
11898         
11899         this.inputEl().dom.value = '';
11900         
11901     },
11902     
11903     onRemoveItem : function(e, _self, o)
11904     {
11905         e.preventDefault();
11906         var index = this.item.indexOf(o.data) * 1;
11907         
11908         if( index < 0){
11909             Roo.log('not this item?!');
11910             return;
11911         }
11912         
11913         this.item.splice(index, 1);
11914         o.item.remove();
11915         
11916         this.syncValue();
11917         
11918         this.fireEvent('remove', this, e);
11919         
11920     },
11921     
11922     syncValue : function()
11923     {
11924         if(!this.item.length){
11925             this.clearValue();
11926             return;
11927         }
11928             
11929         var value = [];
11930         var _this = this;
11931         Roo.each(this.item, function(i){
11932             if(_this.valueField){
11933                 value.push(i[_this.valueField]);
11934                 return;
11935             }
11936
11937             value.push(i);
11938         });
11939
11940         this.value = value.join(',');
11941
11942         if(this.hiddenField){
11943             this.hiddenField.dom.value = this.value;
11944         }
11945     },
11946     
11947     clearItem : function()
11948     {
11949         if(!this.multiple){
11950             return;
11951         }
11952         
11953         this.item = [];
11954         
11955         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11956            c.remove();
11957         });
11958         
11959         this.syncValue();
11960     },
11961     
11962     inputEl: function ()
11963     {
11964         if(this.tickable){
11965             return this.searchField;
11966         }
11967         return this.el.select('input.form-control',true).first();
11968     },
11969     
11970     
11971     onTickableFooterButtonClick : function(e, btn, el)
11972     {
11973         e.preventDefault();
11974         
11975         if(btn && btn.name == 'cancel'){
11976             this.tickItems = Roo.apply([], this.item);
11977             this.collapse();
11978             return;
11979         }
11980         
11981         this.clearItem();
11982         
11983         var _this = this;
11984         
11985         Roo.each(this.tickItems, function(o){
11986             _this.addItem(o);
11987         });
11988         
11989         this.collapse();
11990         
11991     }
11992     
11993     
11994
11995     /** 
11996     * @cfg {Boolean} grow 
11997     * @hide 
11998     */
11999     /** 
12000     * @cfg {Number} growMin 
12001     * @hide 
12002     */
12003     /** 
12004     * @cfg {Number} growMax 
12005     * @hide 
12006     */
12007     /**
12008      * @hide
12009      * @method autoSize
12010      */
12011 });
12012 /*
12013  * Based on:
12014  * Ext JS Library 1.1.1
12015  * Copyright(c) 2006-2007, Ext JS, LLC.
12016  *
12017  * Originally Released Under LGPL - original licence link has changed is not relivant.
12018  *
12019  * Fork - LGPL
12020  * <script type="text/javascript">
12021  */
12022
12023 /**
12024  * @class Roo.View
12025  * @extends Roo.util.Observable
12026  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12027  * This class also supports single and multi selection modes. <br>
12028  * Create a data model bound view:
12029  <pre><code>
12030  var store = new Roo.data.Store(...);
12031
12032  var view = new Roo.View({
12033     el : "my-element",
12034     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12035  
12036     singleSelect: true,
12037     selectedClass: "ydataview-selected",
12038     store: store
12039  });
12040
12041  // listen for node click?
12042  view.on("click", function(vw, index, node, e){
12043  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12044  });
12045
12046  // load XML data
12047  dataModel.load("foobar.xml");
12048  </code></pre>
12049  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12050  * <br><br>
12051  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12052  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12053  * 
12054  * Note: old style constructor is still suported (container, template, config)
12055  * 
12056  * @constructor
12057  * Create a new View
12058  * @param {Object} config The config object
12059  * 
12060  */
12061 Roo.View = function(config, depreciated_tpl, depreciated_config){
12062     
12063     this.parent = false;
12064     
12065     if (typeof(depreciated_tpl) == 'undefined') {
12066         // new way.. - universal constructor.
12067         Roo.apply(this, config);
12068         this.el  = Roo.get(this.el);
12069     } else {
12070         // old format..
12071         this.el  = Roo.get(config);
12072         this.tpl = depreciated_tpl;
12073         Roo.apply(this, depreciated_config);
12074     }
12075     this.wrapEl  = this.el.wrap().wrap();
12076     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12077     
12078     
12079     if(typeof(this.tpl) == "string"){
12080         this.tpl = new Roo.Template(this.tpl);
12081     } else {
12082         // support xtype ctors..
12083         this.tpl = new Roo.factory(this.tpl, Roo);
12084     }
12085     
12086     
12087     this.tpl.compile();
12088     
12089     /** @private */
12090     this.addEvents({
12091         /**
12092          * @event beforeclick
12093          * Fires before a click is processed. Returns false to cancel the default action.
12094          * @param {Roo.View} this
12095          * @param {Number} index The index of the target node
12096          * @param {HTMLElement} node The target node
12097          * @param {Roo.EventObject} e The raw event object
12098          */
12099             "beforeclick" : true,
12100         /**
12101          * @event click
12102          * Fires when a template node is clicked.
12103          * @param {Roo.View} this
12104          * @param {Number} index The index of the target node
12105          * @param {HTMLElement} node The target node
12106          * @param {Roo.EventObject} e The raw event object
12107          */
12108             "click" : true,
12109         /**
12110          * @event dblclick
12111          * Fires when a template node is double clicked.
12112          * @param {Roo.View} this
12113          * @param {Number} index The index of the target node
12114          * @param {HTMLElement} node The target node
12115          * @param {Roo.EventObject} e The raw event object
12116          */
12117             "dblclick" : true,
12118         /**
12119          * @event contextmenu
12120          * Fires when a template node is right clicked.
12121          * @param {Roo.View} this
12122          * @param {Number} index The index of the target node
12123          * @param {HTMLElement} node The target node
12124          * @param {Roo.EventObject} e The raw event object
12125          */
12126             "contextmenu" : true,
12127         /**
12128          * @event selectionchange
12129          * Fires when the selected nodes change.
12130          * @param {Roo.View} this
12131          * @param {Array} selections Array of the selected nodes
12132          */
12133             "selectionchange" : true,
12134     
12135         /**
12136          * @event beforeselect
12137          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12138          * @param {Roo.View} this
12139          * @param {HTMLElement} node The node to be selected
12140          * @param {Array} selections Array of currently selected nodes
12141          */
12142             "beforeselect" : true,
12143         /**
12144          * @event preparedata
12145          * Fires on every row to render, to allow you to change the data.
12146          * @param {Roo.View} this
12147          * @param {Object} data to be rendered (change this)
12148          */
12149           "preparedata" : true
12150           
12151           
12152         });
12153
12154
12155
12156     this.el.on({
12157         "click": this.onClick,
12158         "dblclick": this.onDblClick,
12159         "contextmenu": this.onContextMenu,
12160         scope:this
12161     });
12162
12163     this.selections = [];
12164     this.nodes = [];
12165     this.cmp = new Roo.CompositeElementLite([]);
12166     if(this.store){
12167         this.store = Roo.factory(this.store, Roo.data);
12168         this.setStore(this.store, true);
12169     }
12170     
12171     if ( this.footer && this.footer.xtype) {
12172            
12173          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12174         
12175         this.footer.dataSource = this.store
12176         this.footer.container = fctr;
12177         this.footer = Roo.factory(this.footer, Roo);
12178         fctr.insertFirst(this.el);
12179         
12180         // this is a bit insane - as the paging toolbar seems to detach the el..
12181 //        dom.parentNode.parentNode.parentNode
12182          // they get detached?
12183     }
12184     
12185     
12186     Roo.View.superclass.constructor.call(this);
12187     
12188     
12189 };
12190
12191 Roo.extend(Roo.View, Roo.util.Observable, {
12192     
12193      /**
12194      * @cfg {Roo.data.Store} store Data store to load data from.
12195      */
12196     store : false,
12197     
12198     /**
12199      * @cfg {String|Roo.Element} el The container element.
12200      */
12201     el : '',
12202     
12203     /**
12204      * @cfg {String|Roo.Template} tpl The template used by this View 
12205      */
12206     tpl : false,
12207     /**
12208      * @cfg {String} dataName the named area of the template to use as the data area
12209      *                          Works with domtemplates roo-name="name"
12210      */
12211     dataName: false,
12212     /**
12213      * @cfg {String} selectedClass The css class to add to selected nodes
12214      */
12215     selectedClass : "x-view-selected",
12216      /**
12217      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12218      */
12219     emptyText : "",
12220     
12221     /**
12222      * @cfg {String} text to display on mask (default Loading)
12223      */
12224     mask : false,
12225     /**
12226      * @cfg {Boolean} multiSelect Allow multiple selection
12227      */
12228     multiSelect : false,
12229     /**
12230      * @cfg {Boolean} singleSelect Allow single selection
12231      */
12232     singleSelect:  false,
12233     
12234     /**
12235      * @cfg {Boolean} toggleSelect - selecting 
12236      */
12237     toggleSelect : false,
12238     
12239     /**
12240      * @cfg {Boolean} tickable - selecting 
12241      */
12242     tickable : false,
12243     
12244     /**
12245      * Returns the element this view is bound to.
12246      * @return {Roo.Element}
12247      */
12248     getEl : function(){
12249         return this.wrapEl;
12250     },
12251     
12252     
12253
12254     /**
12255      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12256      */
12257     refresh : function(){
12258         Roo.log('refresh');
12259         var t = this.tpl;
12260         
12261         // if we are using something like 'domtemplate', then
12262         // the what gets used is:
12263         // t.applySubtemplate(NAME, data, wrapping data..)
12264         // the outer template then get' applied with
12265         //     the store 'extra data'
12266         // and the body get's added to the
12267         //      roo-name="data" node?
12268         //      <span class='roo-tpl-{name}'></span> ?????
12269         
12270         
12271         
12272         this.clearSelections();
12273         this.el.update("");
12274         var html = [];
12275         var records = this.store.getRange();
12276         if(records.length < 1) {
12277             
12278             // is this valid??  = should it render a template??
12279             
12280             this.el.update(this.emptyText);
12281             return;
12282         }
12283         var el = this.el;
12284         if (this.dataName) {
12285             this.el.update(t.apply(this.store.meta)); //????
12286             el = this.el.child('.roo-tpl-' + this.dataName);
12287         }
12288         
12289         for(var i = 0, len = records.length; i < len; i++){
12290             var data = this.prepareData(records[i].data, i, records[i]);
12291             this.fireEvent("preparedata", this, data, i, records[i]);
12292             
12293             var d = Roo.apply({}, data);
12294             
12295             if(this.tickable){
12296                 Roo.apply(d, {'roo-id' : Roo.id()});
12297                 
12298                 var _this = this;
12299             
12300                 Roo.each(this.parent.item, function(item){
12301                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12302                         return;
12303                     }
12304                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12305                 });
12306             }
12307             
12308             html[html.length] = Roo.util.Format.trim(
12309                 this.dataName ?
12310                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12311                     t.apply(d)
12312             );
12313         }
12314         
12315         
12316         
12317         el.update(html.join(""));
12318         this.nodes = el.dom.childNodes;
12319         this.updateIndexes(0);
12320     },
12321     
12322
12323     /**
12324      * Function to override to reformat the data that is sent to
12325      * the template for each node.
12326      * DEPRICATED - use the preparedata event handler.
12327      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12328      * a JSON object for an UpdateManager bound view).
12329      */
12330     prepareData : function(data, index, record)
12331     {
12332         this.fireEvent("preparedata", this, data, index, record);
12333         return data;
12334     },
12335
12336     onUpdate : function(ds, record){
12337          Roo.log('on update');   
12338         this.clearSelections();
12339         var index = this.store.indexOf(record);
12340         var n = this.nodes[index];
12341         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12342         n.parentNode.removeChild(n);
12343         this.updateIndexes(index, index);
12344     },
12345
12346     
12347     
12348 // --------- FIXME     
12349     onAdd : function(ds, records, index)
12350     {
12351         Roo.log(['on Add', ds, records, index] );        
12352         this.clearSelections();
12353         if(this.nodes.length == 0){
12354             this.refresh();
12355             return;
12356         }
12357         var n = this.nodes[index];
12358         for(var i = 0, len = records.length; i < len; i++){
12359             var d = this.prepareData(records[i].data, i, records[i]);
12360             if(n){
12361                 this.tpl.insertBefore(n, d);
12362             }else{
12363                 
12364                 this.tpl.append(this.el, d);
12365             }
12366         }
12367         this.updateIndexes(index);
12368     },
12369
12370     onRemove : function(ds, record, index){
12371         Roo.log('onRemove');
12372         this.clearSelections();
12373         var el = this.dataName  ?
12374             this.el.child('.roo-tpl-' + this.dataName) :
12375             this.el; 
12376         
12377         el.dom.removeChild(this.nodes[index]);
12378         this.updateIndexes(index);
12379     },
12380
12381     /**
12382      * Refresh an individual node.
12383      * @param {Number} index
12384      */
12385     refreshNode : function(index){
12386         this.onUpdate(this.store, this.store.getAt(index));
12387     },
12388
12389     updateIndexes : function(startIndex, endIndex){
12390         var ns = this.nodes;
12391         startIndex = startIndex || 0;
12392         endIndex = endIndex || ns.length - 1;
12393         for(var i = startIndex; i <= endIndex; i++){
12394             ns[i].nodeIndex = i;
12395         }
12396     },
12397
12398     /**
12399      * Changes the data store this view uses and refresh the view.
12400      * @param {Store} store
12401      */
12402     setStore : function(store, initial){
12403         if(!initial && this.store){
12404             this.store.un("datachanged", this.refresh);
12405             this.store.un("add", this.onAdd);
12406             this.store.un("remove", this.onRemove);
12407             this.store.un("update", this.onUpdate);
12408             this.store.un("clear", this.refresh);
12409             this.store.un("beforeload", this.onBeforeLoad);
12410             this.store.un("load", this.onLoad);
12411             this.store.un("loadexception", this.onLoad);
12412         }
12413         if(store){
12414           
12415             store.on("datachanged", this.refresh, this);
12416             store.on("add", this.onAdd, this);
12417             store.on("remove", this.onRemove, this);
12418             store.on("update", this.onUpdate, this);
12419             store.on("clear", this.refresh, this);
12420             store.on("beforeload", this.onBeforeLoad, this);
12421             store.on("load", this.onLoad, this);
12422             store.on("loadexception", this.onLoad, this);
12423         }
12424         
12425         if(store){
12426             this.refresh();
12427         }
12428     },
12429     /**
12430      * onbeforeLoad - masks the loading area.
12431      *
12432      */
12433     onBeforeLoad : function(store,opts)
12434     {
12435          Roo.log('onBeforeLoad');   
12436         if (!opts.add) {
12437             this.el.update("");
12438         }
12439         this.el.mask(this.mask ? this.mask : "Loading" ); 
12440     },
12441     onLoad : function ()
12442     {
12443         this.el.unmask();
12444     },
12445     
12446
12447     /**
12448      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12449      * @param {HTMLElement} node
12450      * @return {HTMLElement} The template node
12451      */
12452     findItemFromChild : function(node){
12453         var el = this.dataName  ?
12454             this.el.child('.roo-tpl-' + this.dataName,true) :
12455             this.el.dom; 
12456         
12457         if(!node || node.parentNode == el){
12458                     return node;
12459             }
12460             var p = node.parentNode;
12461             while(p && p != el){
12462             if(p.parentNode == el){
12463                 return p;
12464             }
12465             p = p.parentNode;
12466         }
12467             return null;
12468     },
12469
12470     /** @ignore */
12471     onClick : function(e){
12472         var item = this.findItemFromChild(e.getTarget());
12473         if(item){
12474             var index = this.indexOf(item);
12475             if(this.onItemClick(item, index, e) !== false){
12476                 this.fireEvent("click", this, index, item, e);
12477             }
12478         }else{
12479             this.clearSelections();
12480         }
12481     },
12482
12483     /** @ignore */
12484     onContextMenu : function(e){
12485         var item = this.findItemFromChild(e.getTarget());
12486         if(item){
12487             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12488         }
12489     },
12490
12491     /** @ignore */
12492     onDblClick : function(e){
12493         var item = this.findItemFromChild(e.getTarget());
12494         if(item){
12495             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12496         }
12497     },
12498
12499     onItemClick : function(item, index, e)
12500     {
12501         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12502             return false;
12503         }
12504         if (this.toggleSelect) {
12505             var m = this.isSelected(item) ? 'unselect' : 'select';
12506             Roo.log(m);
12507             var _t = this;
12508             _t[m](item, true, false);
12509             return true;
12510         }
12511         if(this.multiSelect || this.singleSelect){
12512             if(this.multiSelect && e.shiftKey && this.lastSelection){
12513                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12514             }else{
12515                 this.select(item, this.multiSelect && e.ctrlKey);
12516                 this.lastSelection = item;
12517             }
12518             
12519             if(!this.tickable){
12520                 e.preventDefault();
12521             }
12522             
12523         }
12524         return true;
12525     },
12526
12527     /**
12528      * Get the number of selected nodes.
12529      * @return {Number}
12530      */
12531     getSelectionCount : function(){
12532         return this.selections.length;
12533     },
12534
12535     /**
12536      * Get the currently selected nodes.
12537      * @return {Array} An array of HTMLElements
12538      */
12539     getSelectedNodes : function(){
12540         return this.selections;
12541     },
12542
12543     /**
12544      * Get the indexes of the selected nodes.
12545      * @return {Array}
12546      */
12547     getSelectedIndexes : function(){
12548         var indexes = [], s = this.selections;
12549         for(var i = 0, len = s.length; i < len; i++){
12550             indexes.push(s[i].nodeIndex);
12551         }
12552         return indexes;
12553     },
12554
12555     /**
12556      * Clear all selections
12557      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12558      */
12559     clearSelections : function(suppressEvent){
12560         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12561             this.cmp.elements = this.selections;
12562             this.cmp.removeClass(this.selectedClass);
12563             this.selections = [];
12564             if(!suppressEvent){
12565                 this.fireEvent("selectionchange", this, this.selections);
12566             }
12567         }
12568     },
12569
12570     /**
12571      * Returns true if the passed node is selected
12572      * @param {HTMLElement/Number} node The node or node index
12573      * @return {Boolean}
12574      */
12575     isSelected : function(node){
12576         var s = this.selections;
12577         if(s.length < 1){
12578             return false;
12579         }
12580         node = this.getNode(node);
12581         return s.indexOf(node) !== -1;
12582     },
12583
12584     /**
12585      * Selects nodes.
12586      * @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
12587      * @param {Boolean} keepExisting (optional) true to keep existing selections
12588      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12589      */
12590     select : function(nodeInfo, keepExisting, suppressEvent){
12591         if(nodeInfo instanceof Array){
12592             if(!keepExisting){
12593                 this.clearSelections(true);
12594             }
12595             for(var i = 0, len = nodeInfo.length; i < len; i++){
12596                 this.select(nodeInfo[i], true, true);
12597             }
12598             return;
12599         } 
12600         var node = this.getNode(nodeInfo);
12601         if(!node || this.isSelected(node)){
12602             return; // already selected.
12603         }
12604         if(!keepExisting){
12605             this.clearSelections(true);
12606         }
12607         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12608             Roo.fly(node).addClass(this.selectedClass);
12609             this.selections.push(node);
12610             if(!suppressEvent){
12611                 this.fireEvent("selectionchange", this, this.selections);
12612             }
12613         }
12614         
12615         
12616     },
12617       /**
12618      * Unselects nodes.
12619      * @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
12620      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12621      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12622      */
12623     unselect : function(nodeInfo, keepExisting, suppressEvent)
12624     {
12625         if(nodeInfo instanceof Array){
12626             Roo.each(this.selections, function(s) {
12627                 this.unselect(s, nodeInfo);
12628             }, this);
12629             return;
12630         }
12631         var node = this.getNode(nodeInfo);
12632         if(!node || !this.isSelected(node)){
12633             Roo.log("not selected");
12634             return; // not selected.
12635         }
12636         // fireevent???
12637         var ns = [];
12638         Roo.each(this.selections, function(s) {
12639             if (s == node ) {
12640                 Roo.fly(node).removeClass(this.selectedClass);
12641
12642                 return;
12643             }
12644             ns.push(s);
12645         },this);
12646         
12647         this.selections= ns;
12648         this.fireEvent("selectionchange", this, this.selections);
12649     },
12650
12651     /**
12652      * Gets a template node.
12653      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12654      * @return {HTMLElement} The node or null if it wasn't found
12655      */
12656     getNode : function(nodeInfo){
12657         if(typeof nodeInfo == "string"){
12658             return document.getElementById(nodeInfo);
12659         }else if(typeof nodeInfo == "number"){
12660             return this.nodes[nodeInfo];
12661         }
12662         return nodeInfo;
12663     },
12664
12665     /**
12666      * Gets a range template nodes.
12667      * @param {Number} startIndex
12668      * @param {Number} endIndex
12669      * @return {Array} An array of nodes
12670      */
12671     getNodes : function(start, end){
12672         var ns = this.nodes;
12673         start = start || 0;
12674         end = typeof end == "undefined" ? ns.length - 1 : end;
12675         var nodes = [];
12676         if(start <= end){
12677             for(var i = start; i <= end; i++){
12678                 nodes.push(ns[i]);
12679             }
12680         } else{
12681             for(var i = start; i >= end; i--){
12682                 nodes.push(ns[i]);
12683             }
12684         }
12685         return nodes;
12686     },
12687
12688     /**
12689      * Finds the index of the passed node
12690      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12691      * @return {Number} The index of the node or -1
12692      */
12693     indexOf : function(node){
12694         node = this.getNode(node);
12695         if(typeof node.nodeIndex == "number"){
12696             return node.nodeIndex;
12697         }
12698         var ns = this.nodes;
12699         for(var i = 0, len = ns.length; i < len; i++){
12700             if(ns[i] == node){
12701                 return i;
12702             }
12703         }
12704         return -1;
12705     }
12706 });
12707 /*
12708  * - LGPL
12709  *
12710  * based on jquery fullcalendar
12711  * 
12712  */
12713
12714 Roo.bootstrap = Roo.bootstrap || {};
12715 /**
12716  * @class Roo.bootstrap.Calendar
12717  * @extends Roo.bootstrap.Component
12718  * Bootstrap Calendar class
12719  * @cfg {Boolean} loadMask (true|false) default false
12720  * @cfg {Object} header generate the user specific header of the calendar, default false
12721
12722  * @constructor
12723  * Create a new Container
12724  * @param {Object} config The config object
12725  */
12726
12727
12728
12729 Roo.bootstrap.Calendar = function(config){
12730     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12731      this.addEvents({
12732         /**
12733              * @event select
12734              * Fires when a date is selected
12735              * @param {DatePicker} this
12736              * @param {Date} date The selected date
12737              */
12738         'select': true,
12739         /**
12740              * @event monthchange
12741              * Fires when the displayed month changes 
12742              * @param {DatePicker} this
12743              * @param {Date} date The selected month
12744              */
12745         'monthchange': true,
12746         /**
12747              * @event evententer
12748              * Fires when mouse over an event
12749              * @param {Calendar} this
12750              * @param {event} Event
12751              */
12752         'evententer': true,
12753         /**
12754              * @event eventleave
12755              * Fires when the mouse leaves an
12756              * @param {Calendar} this
12757              * @param {event}
12758              */
12759         'eventleave': true,
12760         /**
12761              * @event eventclick
12762              * Fires when the mouse click an
12763              * @param {Calendar} this
12764              * @param {event}
12765              */
12766         'eventclick': true
12767         
12768     });
12769
12770 };
12771
12772 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12773     
12774      /**
12775      * @cfg {Number} startDay
12776      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12777      */
12778     startDay : 0,
12779     
12780     loadMask : false,
12781     
12782     header : false,
12783       
12784     getAutoCreate : function(){
12785         
12786         
12787         var fc_button = function(name, corner, style, content ) {
12788             return Roo.apply({},{
12789                 tag : 'span',
12790                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12791                          (corner.length ?
12792                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12793                             ''
12794                         ),
12795                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12796                 unselectable: 'on'
12797             });
12798         };
12799         
12800         var header = {};
12801         
12802         if(!this.header){
12803             header = {
12804                 tag : 'table',
12805                 cls : 'fc-header',
12806                 style : 'width:100%',
12807                 cn : [
12808                     {
12809                         tag: 'tr',
12810                         cn : [
12811                             {
12812                                 tag : 'td',
12813                                 cls : 'fc-header-left',
12814                                 cn : [
12815                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12816                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12817                                     { tag: 'span', cls: 'fc-header-space' },
12818                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12819
12820
12821                                 ]
12822                             },
12823
12824                             {
12825                                 tag : 'td',
12826                                 cls : 'fc-header-center',
12827                                 cn : [
12828                                     {
12829                                         tag: 'span',
12830                                         cls: 'fc-header-title',
12831                                         cn : {
12832                                             tag: 'H2',
12833                                             html : 'month / year'
12834                                         }
12835                                     }
12836
12837                                 ]
12838                             },
12839                             {
12840                                 tag : 'td',
12841                                 cls : 'fc-header-right',
12842                                 cn : [
12843                               /*      fc_button('month', 'left', '', 'month' ),
12844                                     fc_button('week', '', '', 'week' ),
12845                                     fc_button('day', 'right', '', 'day' )
12846                                 */    
12847
12848                                 ]
12849                             }
12850
12851                         ]
12852                     }
12853                 ]
12854             };
12855         }
12856         
12857         header = this.header;
12858         
12859        
12860         var cal_heads = function() {
12861             var ret = [];
12862             // fixme - handle this.
12863             
12864             for (var i =0; i < Date.dayNames.length; i++) {
12865                 var d = Date.dayNames[i];
12866                 ret.push({
12867                     tag: 'th',
12868                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12869                     html : d.substring(0,3)
12870                 });
12871                 
12872             }
12873             ret[0].cls += ' fc-first';
12874             ret[6].cls += ' fc-last';
12875             return ret;
12876         };
12877         var cal_cell = function(n) {
12878             return  {
12879                 tag: 'td',
12880                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12881                 cn : [
12882                     {
12883                         cn : [
12884                             {
12885                                 cls: 'fc-day-number',
12886                                 html: 'D'
12887                             },
12888                             {
12889                                 cls: 'fc-day-content',
12890                              
12891                                 cn : [
12892                                      {
12893                                         style: 'position: relative;' // height: 17px;
12894                                     }
12895                                 ]
12896                             }
12897                             
12898                             
12899                         ]
12900                     }
12901                 ]
12902                 
12903             }
12904         };
12905         var cal_rows = function() {
12906             
12907             var ret = []
12908             for (var r = 0; r < 6; r++) {
12909                 var row= {
12910                     tag : 'tr',
12911                     cls : 'fc-week',
12912                     cn : []
12913                 };
12914                 
12915                 for (var i =0; i < Date.dayNames.length; i++) {
12916                     var d = Date.dayNames[i];
12917                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12918
12919                 }
12920                 row.cn[0].cls+=' fc-first';
12921                 row.cn[0].cn[0].style = 'min-height:90px';
12922                 row.cn[6].cls+=' fc-last';
12923                 ret.push(row);
12924                 
12925             }
12926             ret[0].cls += ' fc-first';
12927             ret[4].cls += ' fc-prev-last';
12928             ret[5].cls += ' fc-last';
12929             return ret;
12930             
12931         };
12932         
12933         var cal_table = {
12934             tag: 'table',
12935             cls: 'fc-border-separate',
12936             style : 'width:100%',
12937             cellspacing  : 0,
12938             cn : [
12939                 { 
12940                     tag: 'thead',
12941                     cn : [
12942                         { 
12943                             tag: 'tr',
12944                             cls : 'fc-first fc-last',
12945                             cn : cal_heads()
12946                         }
12947                     ]
12948                 },
12949                 { 
12950                     tag: 'tbody',
12951                     cn : cal_rows()
12952                 }
12953                   
12954             ]
12955         };
12956          
12957          var cfg = {
12958             cls : 'fc fc-ltr',
12959             cn : [
12960                 header,
12961                 {
12962                     cls : 'fc-content',
12963                     style : "position: relative;",
12964                     cn : [
12965                         {
12966                             cls : 'fc-view fc-view-month fc-grid',
12967                             style : 'position: relative',
12968                             unselectable : 'on',
12969                             cn : [
12970                                 {
12971                                     cls : 'fc-event-container',
12972                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12973                                 },
12974                                 cal_table
12975                             ]
12976                         }
12977                     ]
12978     
12979                 }
12980            ] 
12981             
12982         };
12983         
12984          
12985         
12986         return cfg;
12987     },
12988     
12989     
12990     initEvents : function()
12991     {
12992         if(!this.store){
12993             throw "can not find store for calendar";
12994         }
12995         
12996         var mark = {
12997             tag: "div",
12998             cls:"x-dlg-mask",
12999             style: "text-align:center",
13000             cn: [
13001                 {
13002                     tag: "div",
13003                     style: "background-color:white;width:50%;margin:250 auto",
13004                     cn: [
13005                         {
13006                             tag: "img",
13007                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13008                         },
13009                         {
13010                             tag: "span",
13011                             html: "Loading"
13012                         }
13013                         
13014                     ]
13015                 }
13016             ]
13017         }
13018         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13019         
13020         var size = this.el.select('.fc-content', true).first().getSize();
13021         this.maskEl.setSize(size.width, size.height);
13022         this.maskEl.enableDisplayMode("block");
13023         if(!this.loadMask){
13024             this.maskEl.hide();
13025         }
13026         
13027         this.store = Roo.factory(this.store, Roo.data);
13028         this.store.on('load', this.onLoad, this);
13029         this.store.on('beforeload', this.onBeforeLoad, this);
13030         
13031         this.resize();
13032         
13033         this.cells = this.el.select('.fc-day',true);
13034         //Roo.log(this.cells);
13035         this.textNodes = this.el.query('.fc-day-number');
13036         this.cells.addClassOnOver('fc-state-hover');
13037         
13038         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13039         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13040         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13041         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13042         
13043         this.on('monthchange', this.onMonthChange, this);
13044         
13045         this.update(new Date().clearTime());
13046     },
13047     
13048     resize : function() {
13049         var sz  = this.el.getSize();
13050         
13051         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13052         this.el.select('.fc-day-content div',true).setHeight(34);
13053     },
13054     
13055     
13056     // private
13057     showPrevMonth : function(e){
13058         this.update(this.activeDate.add("mo", -1));
13059     },
13060     showToday : function(e){
13061         this.update(new Date().clearTime());
13062     },
13063     // private
13064     showNextMonth : function(e){
13065         this.update(this.activeDate.add("mo", 1));
13066     },
13067
13068     // private
13069     showPrevYear : function(){
13070         this.update(this.activeDate.add("y", -1));
13071     },
13072
13073     // private
13074     showNextYear : function(){
13075         this.update(this.activeDate.add("y", 1));
13076     },
13077
13078     
13079    // private
13080     update : function(date)
13081     {
13082         var vd = this.activeDate;
13083         this.activeDate = date;
13084 //        if(vd && this.el){
13085 //            var t = date.getTime();
13086 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13087 //                Roo.log('using add remove');
13088 //                
13089 //                this.fireEvent('monthchange', this, date);
13090 //                
13091 //                this.cells.removeClass("fc-state-highlight");
13092 //                this.cells.each(function(c){
13093 //                   if(c.dateValue == t){
13094 //                       c.addClass("fc-state-highlight");
13095 //                       setTimeout(function(){
13096 //                            try{c.dom.firstChild.focus();}catch(e){}
13097 //                       }, 50);
13098 //                       return false;
13099 //                   }
13100 //                   return true;
13101 //                });
13102 //                return;
13103 //            }
13104 //        }
13105         
13106         var days = date.getDaysInMonth();
13107         
13108         var firstOfMonth = date.getFirstDateOfMonth();
13109         var startingPos = firstOfMonth.getDay()-this.startDay;
13110         
13111         if(startingPos < this.startDay){
13112             startingPos += 7;
13113         }
13114         
13115         var pm = date.add(Date.MONTH, -1);
13116         var prevStart = pm.getDaysInMonth()-startingPos;
13117 //        
13118         this.cells = this.el.select('.fc-day',true);
13119         this.textNodes = this.el.query('.fc-day-number');
13120         this.cells.addClassOnOver('fc-state-hover');
13121         
13122         var cells = this.cells.elements;
13123         var textEls = this.textNodes;
13124         
13125         Roo.each(cells, function(cell){
13126             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13127         });
13128         
13129         days += startingPos;
13130
13131         // convert everything to numbers so it's fast
13132         var day = 86400000;
13133         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13134         //Roo.log(d);
13135         //Roo.log(pm);
13136         //Roo.log(prevStart);
13137         
13138         var today = new Date().clearTime().getTime();
13139         var sel = date.clearTime().getTime();
13140         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13141         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13142         var ddMatch = this.disabledDatesRE;
13143         var ddText = this.disabledDatesText;
13144         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13145         var ddaysText = this.disabledDaysText;
13146         var format = this.format;
13147         
13148         var setCellClass = function(cal, cell){
13149             cell.row = 0;
13150             cell.events = [];
13151             cell.more = [];
13152             //Roo.log('set Cell Class');
13153             cell.title = "";
13154             var t = d.getTime();
13155             
13156             //Roo.log(d);
13157             
13158             cell.dateValue = t;
13159             if(t == today){
13160                 cell.className += " fc-today";
13161                 cell.className += " fc-state-highlight";
13162                 cell.title = cal.todayText;
13163             }
13164             if(t == sel){
13165                 // disable highlight in other month..
13166                 //cell.className += " fc-state-highlight";
13167                 
13168             }
13169             // disabling
13170             if(t < min) {
13171                 cell.className = " fc-state-disabled";
13172                 cell.title = cal.minText;
13173                 return;
13174             }
13175             if(t > max) {
13176                 cell.className = " fc-state-disabled";
13177                 cell.title = cal.maxText;
13178                 return;
13179             }
13180             if(ddays){
13181                 if(ddays.indexOf(d.getDay()) != -1){
13182                     cell.title = ddaysText;
13183                     cell.className = " fc-state-disabled";
13184                 }
13185             }
13186             if(ddMatch && format){
13187                 var fvalue = d.dateFormat(format);
13188                 if(ddMatch.test(fvalue)){
13189                     cell.title = ddText.replace("%0", fvalue);
13190                     cell.className = " fc-state-disabled";
13191                 }
13192             }
13193             
13194             if (!cell.initialClassName) {
13195                 cell.initialClassName = cell.dom.className;
13196             }
13197             
13198             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13199         };
13200
13201         var i = 0;
13202         
13203         for(; i < startingPos; i++) {
13204             textEls[i].innerHTML = (++prevStart);
13205             d.setDate(d.getDate()+1);
13206             
13207             cells[i].className = "fc-past fc-other-month";
13208             setCellClass(this, cells[i]);
13209         }
13210         
13211         var intDay = 0;
13212         
13213         for(; i < days; i++){
13214             intDay = i - startingPos + 1;
13215             textEls[i].innerHTML = (intDay);
13216             d.setDate(d.getDate()+1);
13217             
13218             cells[i].className = ''; // "x-date-active";
13219             setCellClass(this, cells[i]);
13220         }
13221         var extraDays = 0;
13222         
13223         for(; i < 42; i++) {
13224             textEls[i].innerHTML = (++extraDays);
13225             d.setDate(d.getDate()+1);
13226             
13227             cells[i].className = "fc-future fc-other-month";
13228             setCellClass(this, cells[i]);
13229         }
13230         
13231         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13232         
13233         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13234         
13235         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13236         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13237         
13238         if(totalRows != 6){
13239             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13240             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13241         }
13242         
13243         this.fireEvent('monthchange', this, date);
13244         
13245         
13246         /*
13247         if(!this.internalRender){
13248             var main = this.el.dom.firstChild;
13249             var w = main.offsetWidth;
13250             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13251             Roo.fly(main).setWidth(w);
13252             this.internalRender = true;
13253             // opera does not respect the auto grow header center column
13254             // then, after it gets a width opera refuses to recalculate
13255             // without a second pass
13256             if(Roo.isOpera && !this.secondPass){
13257                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13258                 this.secondPass = true;
13259                 this.update.defer(10, this, [date]);
13260             }
13261         }
13262         */
13263         
13264     },
13265     
13266     findCell : function(dt) {
13267         dt = dt.clearTime().getTime();
13268         var ret = false;
13269         this.cells.each(function(c){
13270             //Roo.log("check " +c.dateValue + '?=' + dt);
13271             if(c.dateValue == dt){
13272                 ret = c;
13273                 return false;
13274             }
13275             return true;
13276         });
13277         
13278         return ret;
13279     },
13280     
13281     findCells : function(ev) {
13282         var s = ev.start.clone().clearTime().getTime();
13283        // Roo.log(s);
13284         var e= ev.end.clone().clearTime().getTime();
13285        // Roo.log(e);
13286         var ret = [];
13287         this.cells.each(function(c){
13288              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13289             
13290             if(c.dateValue > e){
13291                 return ;
13292             }
13293             if(c.dateValue < s){
13294                 return ;
13295             }
13296             ret.push(c);
13297         });
13298         
13299         return ret;    
13300     },
13301     
13302 //    findBestRow: function(cells)
13303 //    {
13304 //        var ret = 0;
13305 //        
13306 //        for (var i =0 ; i < cells.length;i++) {
13307 //            ret  = Math.max(cells[i].rows || 0,ret);
13308 //        }
13309 //        return ret;
13310 //        
13311 //    },
13312     
13313     
13314     addItem : function(ev)
13315     {
13316         // look for vertical location slot in
13317         var cells = this.findCells(ev);
13318         
13319 //        ev.row = this.findBestRow(cells);
13320         
13321         // work out the location.
13322         
13323         var crow = false;
13324         var rows = [];
13325         for(var i =0; i < cells.length; i++) {
13326             
13327             cells[i].row = cells[0].row;
13328             
13329             if(i == 0){
13330                 cells[i].row = cells[i].row + 1;
13331             }
13332             
13333             if (!crow) {
13334                 crow = {
13335                     start : cells[i],
13336                     end :  cells[i]
13337                 };
13338                 continue;
13339             }
13340             if (crow.start.getY() == cells[i].getY()) {
13341                 // on same row.
13342                 crow.end = cells[i];
13343                 continue;
13344             }
13345             // different row.
13346             rows.push(crow);
13347             crow = {
13348                 start: cells[i],
13349                 end : cells[i]
13350             };
13351             
13352         }
13353         
13354         rows.push(crow);
13355         ev.els = [];
13356         ev.rows = rows;
13357         ev.cells = cells;
13358         
13359         cells[0].events.push(ev);
13360         
13361         this.calevents.push(ev);
13362     },
13363     
13364     clearEvents: function() {
13365         
13366         if(!this.calevents){
13367             return;
13368         }
13369         
13370         Roo.each(this.cells.elements, function(c){
13371             c.row = 0;
13372             c.events = [];
13373             c.more = [];
13374         });
13375         
13376         Roo.each(this.calevents, function(e) {
13377             Roo.each(e.els, function(el) {
13378                 el.un('mouseenter' ,this.onEventEnter, this);
13379                 el.un('mouseleave' ,this.onEventLeave, this);
13380                 el.remove();
13381             },this);
13382         },this);
13383         
13384         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13385             e.remove();
13386         });
13387         
13388     },
13389     
13390     renderEvents: function()
13391     {   
13392         var _this = this;
13393         
13394         this.cells.each(function(c) {
13395             
13396             if(c.row < 5){
13397                 return;
13398             }
13399             
13400             var ev = c.events;
13401             
13402             var r = 4;
13403             if(c.row != c.events.length){
13404                 r = 4 - (4 - (c.row - c.events.length));
13405             }
13406             
13407             c.events = ev.slice(0, r);
13408             c.more = ev.slice(r);
13409             
13410             if(c.more.length && c.more.length == 1){
13411                 c.events.push(c.more.pop());
13412             }
13413             
13414             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13415             
13416         });
13417             
13418         this.cells.each(function(c) {
13419             
13420             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13421             
13422             
13423             for (var e = 0; e < c.events.length; e++){
13424                 var ev = c.events[e];
13425                 var rows = ev.rows;
13426                 
13427                 for(var i = 0; i < rows.length; i++) {
13428                 
13429                     // how many rows should it span..
13430
13431                     var  cfg = {
13432                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13433                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13434
13435                         unselectable : "on",
13436                         cn : [
13437                             {
13438                                 cls: 'fc-event-inner',
13439                                 cn : [
13440     //                                {
13441     //                                  tag:'span',
13442     //                                  cls: 'fc-event-time',
13443     //                                  html : cells.length > 1 ? '' : ev.time
13444     //                                },
13445                                     {
13446                                       tag:'span',
13447                                       cls: 'fc-event-title',
13448                                       html : String.format('{0}', ev.title)
13449                                     }
13450
13451
13452                                 ]
13453                             },
13454                             {
13455                                 cls: 'ui-resizable-handle ui-resizable-e',
13456                                 html : '&nbsp;&nbsp;&nbsp'
13457                             }
13458
13459                         ]
13460                     };
13461
13462                     if (i == 0) {
13463                         cfg.cls += ' fc-event-start';
13464                     }
13465                     if ((i+1) == rows.length) {
13466                         cfg.cls += ' fc-event-end';
13467                     }
13468
13469                     var ctr = _this.el.select('.fc-event-container',true).first();
13470                     var cg = ctr.createChild(cfg);
13471
13472                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13473                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13474
13475                     var r = (c.more.length) ? 1 : 0;
13476                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13477                     cg.setWidth(ebox.right - sbox.x -2);
13478
13479                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13480                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13481                     cg.on('click', _this.onEventClick, _this, ev);
13482
13483                     ev.els.push(cg);
13484                     
13485                 }
13486                 
13487             }
13488             
13489             
13490             if(c.more.length){
13491                 var  cfg = {
13492                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13493                     style : 'position: absolute',
13494                     unselectable : "on",
13495                     cn : [
13496                         {
13497                             cls: 'fc-event-inner',
13498                             cn : [
13499                                 {
13500                                   tag:'span',
13501                                   cls: 'fc-event-title',
13502                                   html : 'More'
13503                                 }
13504
13505
13506                             ]
13507                         },
13508                         {
13509                             cls: 'ui-resizable-handle ui-resizable-e',
13510                             html : '&nbsp;&nbsp;&nbsp'
13511                         }
13512
13513                     ]
13514                 };
13515
13516                 var ctr = _this.el.select('.fc-event-container',true).first();
13517                 var cg = ctr.createChild(cfg);
13518
13519                 var sbox = c.select('.fc-day-content',true).first().getBox();
13520                 var ebox = c.select('.fc-day-content',true).first().getBox();
13521                 //Roo.log(cg);
13522                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13523                 cg.setWidth(ebox.right - sbox.x -2);
13524
13525                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13526                 
13527             }
13528             
13529         });
13530         
13531         
13532         
13533     },
13534     
13535     onEventEnter: function (e, el,event,d) {
13536         this.fireEvent('evententer', this, el, event);
13537     },
13538     
13539     onEventLeave: function (e, el,event,d) {
13540         this.fireEvent('eventleave', this, el, event);
13541     },
13542     
13543     onEventClick: function (e, el,event,d) {
13544         this.fireEvent('eventclick', this, el, event);
13545     },
13546     
13547     onMonthChange: function () {
13548         this.store.load();
13549     },
13550     
13551     onMoreEventClick: function(e, el, more)
13552     {
13553         var _this = this;
13554         
13555         this.calpopover.placement = 'right';
13556         this.calpopover.setTitle('More');
13557         
13558         this.calpopover.setContent('');
13559         
13560         var ctr = this.calpopover.el.select('.popover-content', true).first();
13561         
13562         Roo.each(more, function(m){
13563             var cfg = {
13564                 cls : 'fc-event-hori fc-event-draggable',
13565                 html : m.title
13566             }
13567             var cg = ctr.createChild(cfg);
13568             
13569             cg.on('click', _this.onEventClick, _this, m);
13570         });
13571         
13572         this.calpopover.show(el);
13573         
13574         
13575     },
13576     
13577     onLoad: function () 
13578     {   
13579         this.calevents = [];
13580         var cal = this;
13581         
13582         if(this.store.getCount() > 0){
13583             this.store.data.each(function(d){
13584                cal.addItem({
13585                     id : d.data.id,
13586                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13587                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13588                     time : d.data.start_time,
13589                     title : d.data.title,
13590                     description : d.data.description,
13591                     venue : d.data.venue
13592                 });
13593             });
13594         }
13595         
13596         this.renderEvents();
13597         
13598         if(this.calevents.length && this.loadMask){
13599             this.maskEl.hide();
13600         }
13601     },
13602     
13603     onBeforeLoad: function()
13604     {
13605         this.clearEvents();
13606         if(this.loadMask){
13607             this.maskEl.show();
13608         }
13609     }
13610 });
13611
13612  
13613  /*
13614  * - LGPL
13615  *
13616  * element
13617  * 
13618  */
13619
13620 /**
13621  * @class Roo.bootstrap.Popover
13622  * @extends Roo.bootstrap.Component
13623  * Bootstrap Popover class
13624  * @cfg {String} html contents of the popover   (or false to use children..)
13625  * @cfg {String} title of popover (or false to hide)
13626  * @cfg {String} placement how it is placed
13627  * @cfg {String} trigger click || hover (or false to trigger manually)
13628  * @cfg {String} over what (parent or false to trigger manually.)
13629  * 
13630  * @constructor
13631  * Create a new Popover
13632  * @param {Object} config The config object
13633  */
13634
13635 Roo.bootstrap.Popover = function(config){
13636     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13637 };
13638
13639 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13640     
13641     title: 'Fill in a title',
13642     html: false,
13643     
13644     placement : 'right',
13645     trigger : 'hover', // hover
13646     
13647     over: 'parent',
13648     
13649     can_build_overlaid : false,
13650     
13651     getChildContainer : function()
13652     {
13653         return this.el.select('.popover-content',true).first();
13654     },
13655     
13656     getAutoCreate : function(){
13657          Roo.log('make popover?');
13658         var cfg = {
13659            cls : 'popover roo-dynamic',
13660            style: 'display:block',
13661            cn : [
13662                 {
13663                     cls : 'arrow'
13664                 },
13665                 {
13666                     cls : 'popover-inner',
13667                     cn : [
13668                         {
13669                             tag: 'h3',
13670                             cls: 'popover-title',
13671                             html : this.title
13672                         },
13673                         {
13674                             cls : 'popover-content',
13675                             html : this.html
13676                         }
13677                     ]
13678                     
13679                 }
13680            ]
13681         };
13682         
13683         return cfg;
13684     },
13685     setTitle: function(str)
13686     {
13687         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13688     },
13689     setContent: function(str)
13690     {
13691         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13692     },
13693     // as it get's added to the bottom of the page.
13694     onRender : function(ct, position)
13695     {
13696         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13697         if(!this.el){
13698             var cfg = Roo.apply({},  this.getAutoCreate());
13699             cfg.id = Roo.id();
13700             
13701             if (this.cls) {
13702                 cfg.cls += ' ' + this.cls;
13703             }
13704             if (this.style) {
13705                 cfg.style = this.style;
13706             }
13707             Roo.log("adding to ")
13708             this.el = Roo.get(document.body).createChild(cfg, position);
13709             Roo.log(this.el);
13710         }
13711         this.initEvents();
13712     },
13713     
13714     initEvents : function()
13715     {
13716         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13717         this.el.enableDisplayMode('block');
13718         this.el.hide();
13719         if (this.over === false) {
13720             return; 
13721         }
13722         if (this.triggers === false) {
13723             return;
13724         }
13725         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13726         var triggers = this.trigger ? this.trigger.split(' ') : [];
13727         Roo.each(triggers, function(trigger) {
13728         
13729             if (trigger == 'click') {
13730                 on_el.on('click', this.toggle, this);
13731             } else if (trigger != 'manual') {
13732                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13733                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13734       
13735                 on_el.on(eventIn  ,this.enter, this);
13736                 on_el.on(eventOut, this.leave, this);
13737             }
13738         }, this);
13739         
13740     },
13741     
13742     
13743     // private
13744     timeout : null,
13745     hoverState : null,
13746     
13747     toggle : function () {
13748         this.hoverState == 'in' ? this.leave() : this.enter();
13749     },
13750     
13751     enter : function () {
13752        
13753     
13754         clearTimeout(this.timeout);
13755     
13756         this.hoverState = 'in'
13757     
13758         if (!this.delay || !this.delay.show) {
13759             this.show();
13760             return 
13761         }
13762         var _t = this;
13763         this.timeout = setTimeout(function () {
13764             if (_t.hoverState == 'in') {
13765                 _t.show();
13766             }
13767         }, this.delay.show)
13768     },
13769     leave : function() {
13770         clearTimeout(this.timeout);
13771     
13772         this.hoverState = 'out'
13773     
13774         if (!this.delay || !this.delay.hide) {
13775             this.hide();
13776             return 
13777         }
13778         var _t = this;
13779         this.timeout = setTimeout(function () {
13780             if (_t.hoverState == 'out') {
13781                 _t.hide();
13782             }
13783         }, this.delay.hide)
13784     },
13785     
13786     show : function (on_el)
13787     {
13788         if (!on_el) {
13789             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13790         }
13791         // set content.
13792         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13793         if (this.html !== false) {
13794             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13795         }
13796         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13797         if (!this.title.length) {
13798             this.el.select('.popover-title',true).hide();
13799         }
13800         
13801         var placement = typeof this.placement == 'function' ?
13802             this.placement.call(this, this.el, on_el) :
13803             this.placement;
13804             
13805         var autoToken = /\s?auto?\s?/i;
13806         var autoPlace = autoToken.test(placement);
13807         if (autoPlace) {
13808             placement = placement.replace(autoToken, '') || 'top';
13809         }
13810         
13811         //this.el.detach()
13812         //this.el.setXY([0,0]);
13813         this.el.show();
13814         this.el.dom.style.display='block';
13815         this.el.addClass(placement);
13816         
13817         //this.el.appendTo(on_el);
13818         
13819         var p = this.getPosition();
13820         var box = this.el.getBox();
13821         
13822         if (autoPlace) {
13823             // fixme..
13824         }
13825         var align = Roo.bootstrap.Popover.alignment[placement]
13826         this.el.alignTo(on_el, align[0],align[1]);
13827         //var arrow = this.el.select('.arrow',true).first();
13828         //arrow.set(align[2], 
13829         
13830         this.el.addClass('in');
13831         this.hoverState = null;
13832         
13833         if (this.el.hasClass('fade')) {
13834             // fade it?
13835         }
13836         
13837     },
13838     hide : function()
13839     {
13840         this.el.setXY([0,0]);
13841         this.el.removeClass('in');
13842         this.el.hide();
13843         
13844     }
13845     
13846 });
13847
13848 Roo.bootstrap.Popover.alignment = {
13849     'left' : ['r-l', [-10,0], 'right'],
13850     'right' : ['l-r', [10,0], 'left'],
13851     'bottom' : ['t-b', [0,10], 'top'],
13852     'top' : [ 'b-t', [0,-10], 'bottom']
13853 };
13854
13855  /*
13856  * - LGPL
13857  *
13858  * Progress
13859  * 
13860  */
13861
13862 /**
13863  * @class Roo.bootstrap.Progress
13864  * @extends Roo.bootstrap.Component
13865  * Bootstrap Progress class
13866  * @cfg {Boolean} striped striped of the progress bar
13867  * @cfg {Boolean} active animated of the progress bar
13868  * 
13869  * 
13870  * @constructor
13871  * Create a new Progress
13872  * @param {Object} config The config object
13873  */
13874
13875 Roo.bootstrap.Progress = function(config){
13876     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13877 };
13878
13879 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13880     
13881     striped : false,
13882     active: false,
13883     
13884     getAutoCreate : function(){
13885         var cfg = {
13886             tag: 'div',
13887             cls: 'progress'
13888         };
13889         
13890         
13891         if(this.striped){
13892             cfg.cls += ' progress-striped';
13893         }
13894       
13895         if(this.active){
13896             cfg.cls += ' active';
13897         }
13898         
13899         
13900         return cfg;
13901     }
13902    
13903 });
13904
13905  
13906
13907  /*
13908  * - LGPL
13909  *
13910  * ProgressBar
13911  * 
13912  */
13913
13914 /**
13915  * @class Roo.bootstrap.ProgressBar
13916  * @extends Roo.bootstrap.Component
13917  * Bootstrap ProgressBar class
13918  * @cfg {Number} aria_valuenow aria-value now
13919  * @cfg {Number} aria_valuemin aria-value min
13920  * @cfg {Number} aria_valuemax aria-value max
13921  * @cfg {String} label label for the progress bar
13922  * @cfg {String} panel (success | info | warning | danger )
13923  * @cfg {String} role role of the progress bar
13924  * @cfg {String} sr_only text
13925  * 
13926  * 
13927  * @constructor
13928  * Create a new ProgressBar
13929  * @param {Object} config The config object
13930  */
13931
13932 Roo.bootstrap.ProgressBar = function(config){
13933     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13934 };
13935
13936 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13937     
13938     aria_valuenow : 0,
13939     aria_valuemin : 0,
13940     aria_valuemax : 100,
13941     label : false,
13942     panel : false,
13943     role : false,
13944     sr_only: false,
13945     
13946     getAutoCreate : function()
13947     {
13948         
13949         var cfg = {
13950             tag: 'div',
13951             cls: 'progress-bar',
13952             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13953         };
13954         
13955         if(this.sr_only){
13956             cfg.cn = {
13957                 tag: 'span',
13958                 cls: 'sr-only',
13959                 html: this.sr_only
13960             }
13961         }
13962         
13963         if(this.role){
13964             cfg.role = this.role;
13965         }
13966         
13967         if(this.aria_valuenow){
13968             cfg['aria-valuenow'] = this.aria_valuenow;
13969         }
13970         
13971         if(this.aria_valuemin){
13972             cfg['aria-valuemin'] = this.aria_valuemin;
13973         }
13974         
13975         if(this.aria_valuemax){
13976             cfg['aria-valuemax'] = this.aria_valuemax;
13977         }
13978         
13979         if(this.label && !this.sr_only){
13980             cfg.html = this.label;
13981         }
13982         
13983         if(this.panel){
13984             cfg.cls += ' progress-bar-' + this.panel;
13985         }
13986         
13987         return cfg;
13988     },
13989     
13990     update : function(aria_valuenow)
13991     {
13992         this.aria_valuenow = aria_valuenow;
13993         
13994         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13995     }
13996    
13997 });
13998
13999  
14000
14001  /*
14002  * - LGPL
14003  *
14004  * column
14005  * 
14006  */
14007
14008 /**
14009  * @class Roo.bootstrap.TabGroup
14010  * @extends Roo.bootstrap.Column
14011  * Bootstrap Column class
14012  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14013  * @cfg {Boolean} carousel true to make the group behave like a carousel
14014  * 
14015  * @constructor
14016  * Create a new TabGroup
14017  * @param {Object} config The config object
14018  */
14019
14020 Roo.bootstrap.TabGroup = function(config){
14021     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14022     if (!this.navId) {
14023         this.navId = Roo.id();
14024     }
14025     this.tabs = [];
14026     Roo.bootstrap.TabGroup.register(this);
14027     
14028 };
14029
14030 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14031     
14032     carousel : false,
14033     transition : false,
14034      
14035     getAutoCreate : function()
14036     {
14037         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14038         
14039         cfg.cls += ' tab-content';
14040         
14041         if (this.carousel) {
14042             cfg.cls += ' carousel slide';
14043             cfg.cn = [{
14044                cls : 'carousel-inner'
14045             }]
14046         }
14047         
14048         
14049         return cfg;
14050     },
14051     getChildContainer : function()
14052     {
14053         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14054     },
14055     
14056     /**
14057     * register a Navigation item
14058     * @param {Roo.bootstrap.NavItem} the navitem to add
14059     */
14060     register : function(item)
14061     {
14062         this.tabs.push( item);
14063         item.navId = this.navId; // not really needed..
14064     
14065     },
14066     
14067     getActivePanel : function()
14068     {
14069         var r = false;
14070         Roo.each(this.tabs, function(t) {
14071             if (t.active) {
14072                 r = t;
14073                 return false;
14074             }
14075             return null;
14076         });
14077         return r;
14078         
14079     },
14080     getPanelByName : function(n)
14081     {
14082         var r = false;
14083         Roo.each(this.tabs, function(t) {
14084             if (t.tabId == n) {
14085                 r = t;
14086                 return false;
14087             }
14088             return null;
14089         });
14090         return r;
14091     },
14092     indexOfPanel : function(p)
14093     {
14094         var r = false;
14095         Roo.each(this.tabs, function(t,i) {
14096             if (t.tabId == p.tabId) {
14097                 r = i;
14098                 return false;
14099             }
14100             return null;
14101         });
14102         return r;
14103     },
14104     /**
14105      * show a specific panel
14106      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14107      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14108      */
14109     showPanel : function (pan)
14110     {
14111         
14112         if (typeof(pan) == 'number') {
14113             pan = this.tabs[pan];
14114         }
14115         if (typeof(pan) == 'string') {
14116             pan = this.getPanelByName(pan);
14117         }
14118         if (pan.tabId == this.getActivePanel().tabId) {
14119             return true;
14120         }
14121         var cur = this.getActivePanel();
14122         
14123         if (false === cur.fireEvent('beforedeactivate')) {
14124             return false;
14125         }
14126         
14127         if (this.carousel) {
14128             this.transition = true;
14129             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14130             var lr = dir == 'next' ? 'left' : 'right';
14131             pan.el.addClass(dir); // or prev
14132             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14133             cur.el.addClass(lr); // or right
14134             pan.el.addClass(lr);
14135             
14136             var _this = this;
14137             cur.el.on('transitionend', function() {
14138                 Roo.log("trans end?");
14139                 
14140                 pan.el.removeClass([lr,dir]);
14141                 pan.setActive(true);
14142                 
14143                 cur.el.removeClass([lr]);
14144                 cur.setActive(false);
14145                 
14146                 _this.transition = false;
14147                 
14148             }, this, { single:  true } );
14149             return true;
14150         }
14151         
14152         cur.setActive(false);
14153         pan.setActive(true);
14154         return true;
14155         
14156     },
14157     showPanelNext : function()
14158     {
14159         var i = this.indexOfPanel(this.getActivePanel());
14160         if (i > this.tabs.length) {
14161             return;
14162         }
14163         this.showPanel(this.tabs[i+1]);
14164     },
14165     showPanelPrev : function()
14166     {
14167         var i = this.indexOfPanel(this.getActivePanel());
14168         if (i  < 1) {
14169             return;
14170         }
14171         this.showPanel(this.tabs[i-1]);
14172     }
14173     
14174     
14175   
14176 });
14177
14178  
14179
14180  
14181  
14182 Roo.apply(Roo.bootstrap.TabGroup, {
14183     
14184     groups: {},
14185      /**
14186     * register a Navigation Group
14187     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14188     */
14189     register : function(navgrp)
14190     {
14191         this.groups[navgrp.navId] = navgrp;
14192         
14193     },
14194     /**
14195     * fetch a Navigation Group based on the navigation ID
14196     * if one does not exist , it will get created.
14197     * @param {string} the navgroup to add
14198     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14199     */
14200     get: function(navId) {
14201         if (typeof(this.groups[navId]) == 'undefined') {
14202             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14203         }
14204         return this.groups[navId] ;
14205     }
14206     
14207     
14208     
14209 });
14210
14211  /*
14212  * - LGPL
14213  *
14214  * TabPanel
14215  * 
14216  */
14217
14218 /**
14219  * @class Roo.bootstrap.TabPanel
14220  * @extends Roo.bootstrap.Component
14221  * Bootstrap TabPanel class
14222  * @cfg {Boolean} active panel active
14223  * @cfg {String} html panel content
14224  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14225  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14226  * 
14227  * 
14228  * @constructor
14229  * Create a new TabPanel
14230  * @param {Object} config The config object
14231  */
14232
14233 Roo.bootstrap.TabPanel = function(config){
14234     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14235     this.addEvents({
14236         /**
14237              * @event changed
14238              * Fires when the active status changes
14239              * @param {Roo.bootstrap.TabPanel} this
14240              * @param {Boolean} state the new state
14241             
14242          */
14243         'changed': true,
14244         /**
14245              * @event beforedeactivate
14246              * Fires before a tab is de-activated - can be used to do validation on a form.
14247              * @param {Roo.bootstrap.TabPanel} this
14248              * @return {Boolean} false if there is an error
14249             
14250          */
14251         'beforedeactivate': true
14252      });
14253     
14254     this.tabId = this.tabId || Roo.id();
14255   
14256 };
14257
14258 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14259     
14260     active: false,
14261     html: false,
14262     tabId: false,
14263     navId : false,
14264     
14265     getAutoCreate : function(){
14266         var cfg = {
14267             tag: 'div',
14268             // item is needed for carousel - not sure if it has any effect otherwise
14269             cls: 'tab-pane item',
14270             html: this.html || ''
14271         };
14272         
14273         if(this.active){
14274             cfg.cls += ' active';
14275         }
14276         
14277         if(this.tabId){
14278             cfg.tabId = this.tabId;
14279         }
14280         
14281         
14282         return cfg;
14283     },
14284     
14285     initEvents:  function()
14286     {
14287         Roo.log('-------- init events on tab panel ---------');
14288         
14289         var p = this.parent();
14290         this.navId = this.navId || p.navId;
14291         
14292         if (typeof(this.navId) != 'undefined') {
14293             // not really needed.. but just in case.. parent should be a NavGroup.
14294             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14295             Roo.log(['register', tg, this]);
14296             tg.register(this);
14297         }
14298     },
14299     
14300     
14301     onRender : function(ct, position)
14302     {
14303        // Roo.log("Call onRender: " + this.xtype);
14304         
14305         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14306         
14307         
14308         
14309         
14310         
14311     },
14312     
14313     setActive: function(state)
14314     {
14315         Roo.log("panel - set active " + this.tabId + "=" + state);
14316         
14317         this.active = state;
14318         if (!state) {
14319             this.el.removeClass('active');
14320             
14321         } else  if (!this.el.hasClass('active')) {
14322             this.el.addClass('active');
14323         }
14324         this.fireEvent('changed', this, state);
14325     }
14326     
14327     
14328 });
14329  
14330
14331  
14332
14333  /*
14334  * - LGPL
14335  *
14336  * DateField
14337  * 
14338  */
14339
14340 /**
14341  * @class Roo.bootstrap.DateField
14342  * @extends Roo.bootstrap.Input
14343  * Bootstrap DateField class
14344  * @cfg {Number} weekStart default 0
14345  * @cfg {Number} weekStart default 0
14346  * @cfg {Number} viewMode default empty, (months|years)
14347  * @cfg {Number} minViewMode default empty, (months|years)
14348  * @cfg {Number} startDate default -Infinity
14349  * @cfg {Number} endDate default Infinity
14350  * @cfg {Boolean} todayHighlight default false
14351  * @cfg {Boolean} todayBtn default false
14352  * @cfg {Boolean} calendarWeeks default false
14353  * @cfg {Object} daysOfWeekDisabled default empty
14354  * 
14355  * @cfg {Boolean} keyboardNavigation default true
14356  * @cfg {String} language default en
14357  * 
14358  * @constructor
14359  * Create a new DateField
14360  * @param {Object} config The config object
14361  */
14362
14363 Roo.bootstrap.DateField = function(config){
14364     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14365      this.addEvents({
14366             /**
14367              * @event show
14368              * Fires when this field show.
14369              * @param {Roo.bootstrap.DateField} this
14370              * @param {Mixed} date The date value
14371              */
14372             show : true,
14373             /**
14374              * @event show
14375              * Fires when this field hide.
14376              * @param {Roo.bootstrap.DateField} this
14377              * @param {Mixed} date The date value
14378              */
14379             hide : true,
14380             /**
14381              * @event select
14382              * Fires when select a date.
14383              * @param {Roo.bootstrap.DateField} this
14384              * @param {Mixed} date The date value
14385              */
14386             select : true
14387         });
14388 };
14389
14390 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14391     
14392     /**
14393      * @cfg {String} format
14394      * The default date format string which can be overriden for localization support.  The format must be
14395      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14396      */
14397     format : "m/d/y",
14398     /**
14399      * @cfg {String} altFormats
14400      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14401      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14402      */
14403     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14404     
14405     weekStart : 0,
14406     
14407     viewMode : '',
14408     
14409     minViewMode : '',
14410     
14411     todayHighlight : false,
14412     
14413     todayBtn: false,
14414     
14415     language: 'en',
14416     
14417     keyboardNavigation: true,
14418     
14419     calendarWeeks: false,
14420     
14421     startDate: -Infinity,
14422     
14423     endDate: Infinity,
14424     
14425     daysOfWeekDisabled: [],
14426     
14427     _events: [],
14428     
14429     UTCDate: function()
14430     {
14431         return new Date(Date.UTC.apply(Date, arguments));
14432     },
14433     
14434     UTCToday: function()
14435     {
14436         var today = new Date();
14437         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14438     },
14439     
14440     getDate: function() {
14441             var d = this.getUTCDate();
14442             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14443     },
14444     
14445     getUTCDate: function() {
14446             return this.date;
14447     },
14448     
14449     setDate: function(d) {
14450             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14451     },
14452     
14453     setUTCDate: function(d) {
14454             this.date = d;
14455             this.setValue(this.formatDate(this.date));
14456     },
14457         
14458     onRender: function(ct, position)
14459     {
14460         
14461         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14462         
14463         this.language = this.language || 'en';
14464         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14465         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14466         
14467         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14468         this.format = this.format || 'm/d/y';
14469         this.isInline = false;
14470         this.isInput = true;
14471         this.component = this.el.select('.add-on', true).first() || false;
14472         this.component = (this.component && this.component.length === 0) ? false : this.component;
14473         this.hasInput = this.component && this.inputEL().length;
14474         
14475         if (typeof(this.minViewMode === 'string')) {
14476             switch (this.minViewMode) {
14477                 case 'months':
14478                     this.minViewMode = 1;
14479                     break;
14480                 case 'years':
14481                     this.minViewMode = 2;
14482                     break;
14483                 default:
14484                     this.minViewMode = 0;
14485                     break;
14486             }
14487         }
14488         
14489         if (typeof(this.viewMode === 'string')) {
14490             switch (this.viewMode) {
14491                 case 'months':
14492                     this.viewMode = 1;
14493                     break;
14494                 case 'years':
14495                     this.viewMode = 2;
14496                     break;
14497                 default:
14498                     this.viewMode = 0;
14499                     break;
14500             }
14501         }
14502                 
14503         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14504         
14505 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14506         
14507         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14508         
14509         this.picker().on('mousedown', this.onMousedown, this);
14510         this.picker().on('click', this.onClick, this);
14511         
14512         this.picker().addClass('datepicker-dropdown');
14513         
14514         this.startViewMode = this.viewMode;
14515         
14516         
14517         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14518             if(!this.calendarWeeks){
14519                 v.remove();
14520                 return;
14521             };
14522             
14523             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14524             v.attr('colspan', function(i, val){
14525                 return parseInt(val) + 1;
14526             });
14527         })
14528                         
14529         
14530         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14531         
14532         this.setStartDate(this.startDate);
14533         this.setEndDate(this.endDate);
14534         
14535         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14536         
14537         this.fillDow();
14538         this.fillMonths();
14539         this.update();
14540         this.showMode();
14541         
14542         if(this.isInline) {
14543             this.show();
14544         }
14545     },
14546     
14547     picker : function()
14548     {
14549         return this.pickerEl;
14550 //        return this.el.select('.datepicker', true).first();
14551     },
14552     
14553     fillDow: function()
14554     {
14555         var dowCnt = this.weekStart;
14556         
14557         var dow = {
14558             tag: 'tr',
14559             cn: [
14560                 
14561             ]
14562         };
14563         
14564         if(this.calendarWeeks){
14565             dow.cn.push({
14566                 tag: 'th',
14567                 cls: 'cw',
14568                 html: '&nbsp;'
14569             })
14570         }
14571         
14572         while (dowCnt < this.weekStart + 7) {
14573             dow.cn.push({
14574                 tag: 'th',
14575                 cls: 'dow',
14576                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14577             });
14578         }
14579         
14580         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14581     },
14582     
14583     fillMonths: function()
14584     {    
14585         var i = 0
14586         var months = this.picker().select('>.datepicker-months td', true).first();
14587         
14588         months.dom.innerHTML = '';
14589         
14590         while (i < 12) {
14591             var month = {
14592                 tag: 'span',
14593                 cls: 'month',
14594                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14595             }
14596             
14597             months.createChild(month);
14598         }
14599         
14600     },
14601     
14602     update: function()
14603     {
14604         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;
14605         
14606         if (this.date < this.startDate) {
14607             this.viewDate = new Date(this.startDate);
14608         } else if (this.date > this.endDate) {
14609             this.viewDate = new Date(this.endDate);
14610         } else {
14611             this.viewDate = new Date(this.date);
14612         }
14613         
14614         this.fill();
14615     },
14616     
14617     fill: function() 
14618     {
14619         var d = new Date(this.viewDate),
14620                 year = d.getUTCFullYear(),
14621                 month = d.getUTCMonth(),
14622                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14623                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14624                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14625                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14626                 currentDate = this.date && this.date.valueOf(),
14627                 today = this.UTCToday();
14628         
14629         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14630         
14631 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14632         
14633 //        this.picker.select('>tfoot th.today').
14634 //                                              .text(dates[this.language].today)
14635 //                                              .toggle(this.todayBtn !== false);
14636     
14637         this.updateNavArrows();
14638         this.fillMonths();
14639                                                 
14640         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14641         
14642         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14643          
14644         prevMonth.setUTCDate(day);
14645         
14646         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14647         
14648         var nextMonth = new Date(prevMonth);
14649         
14650         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14651         
14652         nextMonth = nextMonth.valueOf();
14653         
14654         var fillMonths = false;
14655         
14656         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14657         
14658         while(prevMonth.valueOf() < nextMonth) {
14659             var clsName = '';
14660             
14661             if (prevMonth.getUTCDay() === this.weekStart) {
14662                 if(fillMonths){
14663                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14664                 }
14665                     
14666                 fillMonths = {
14667                     tag: 'tr',
14668                     cn: []
14669                 };
14670                 
14671                 if(this.calendarWeeks){
14672                     // ISO 8601: First week contains first thursday.
14673                     // ISO also states week starts on Monday, but we can be more abstract here.
14674                     var
14675                     // Start of current week: based on weekstart/current date
14676                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14677                     // Thursday of this week
14678                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14679                     // First Thursday of year, year from thursday
14680                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14681                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14682                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14683                     
14684                     fillMonths.cn.push({
14685                         tag: 'td',
14686                         cls: 'cw',
14687                         html: calWeek
14688                     });
14689                 }
14690             }
14691             
14692             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14693                 clsName += ' old';
14694             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14695                 clsName += ' new';
14696             }
14697             if (this.todayHighlight &&
14698                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14699                 prevMonth.getUTCMonth() == today.getMonth() &&
14700                 prevMonth.getUTCDate() == today.getDate()) {
14701                 clsName += ' today';
14702             }
14703             
14704             if (currentDate && prevMonth.valueOf() === currentDate) {
14705                 clsName += ' active';
14706             }
14707             
14708             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14709                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14710                     clsName += ' disabled';
14711             }
14712             
14713             fillMonths.cn.push({
14714                 tag: 'td',
14715                 cls: 'day ' + clsName,
14716                 html: prevMonth.getDate()
14717             })
14718             
14719             prevMonth.setDate(prevMonth.getDate()+1);
14720         }
14721           
14722         var currentYear = this.date && this.date.getUTCFullYear();
14723         var currentMonth = this.date && this.date.getUTCMonth();
14724         
14725         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14726         
14727         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14728             v.removeClass('active');
14729             
14730             if(currentYear === year && k === currentMonth){
14731                 v.addClass('active');
14732             }
14733             
14734             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14735                 v.addClass('disabled');
14736             }
14737             
14738         });
14739         
14740         
14741         year = parseInt(year/10, 10) * 10;
14742         
14743         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14744         
14745         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14746         
14747         year -= 1;
14748         for (var i = -1; i < 11; i++) {
14749             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14750                 tag: 'span',
14751                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14752                 html: year
14753             })
14754             
14755             year += 1;
14756         }
14757     },
14758     
14759     showMode: function(dir) 
14760     {
14761         if (dir) {
14762             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14763         }
14764         Roo.each(this.picker().select('>div',true).elements, function(v){
14765             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14766             v.hide();
14767         });
14768         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14769     },
14770     
14771     place: function()
14772     {
14773         if(this.isInline) return;
14774         
14775         this.picker().removeClass(['bottom', 'top']);
14776         
14777         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14778             /*
14779              * place to the top of element!
14780              *
14781              */
14782             
14783             this.picker().addClass('top');
14784             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14785             
14786             return;
14787         }
14788         
14789         this.picker().addClass('bottom');
14790         
14791         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14792     },
14793     
14794     parseDate : function(value)
14795     {
14796         if(!value || value instanceof Date){
14797             return value;
14798         }
14799         var v = Date.parseDate(value, this.format);
14800         if (!v && this.useIso) {
14801             v = Date.parseDate(value, 'Y-m-d');
14802         }
14803         if(!v && this.altFormats){
14804             if(!this.altFormatsArray){
14805                 this.altFormatsArray = this.altFormats.split("|");
14806             }
14807             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14808                 v = Date.parseDate(value, this.altFormatsArray[i]);
14809             }
14810         }
14811         return v;
14812     },
14813     
14814     formatDate : function(date, fmt)
14815     {
14816         return (!date || !(date instanceof Date)) ?
14817         date : date.dateFormat(fmt || this.format);
14818     },
14819     
14820     onFocus : function()
14821     {
14822         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14823         this.show();
14824     },
14825     
14826     onBlur : function()
14827     {
14828         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14829         
14830         var d = this.inputEl().getValue();
14831         
14832         this.setValue(d);
14833                 
14834         this.hide();
14835     },
14836     
14837     show : function()
14838     {
14839         this.picker().show();
14840         this.update();
14841         this.place();
14842         
14843         this.fireEvent('show', this, this.date);
14844     },
14845     
14846     hide : function()
14847     {
14848         if(this.isInline) return;
14849         this.picker().hide();
14850         this.viewMode = this.startViewMode;
14851         this.showMode();
14852         
14853         this.fireEvent('hide', this, this.date);
14854         
14855     },
14856     
14857     onMousedown: function(e)
14858     {
14859         e.stopPropagation();
14860         e.preventDefault();
14861     },
14862     
14863     keyup: function(e)
14864     {
14865         Roo.bootstrap.DateField.superclass.keyup.call(this);
14866         this.update();
14867     },
14868
14869     setValue: function(v)
14870     {
14871         var d = new Date(v).clearTime();
14872         
14873         if(isNaN(d.getTime())){
14874             this.date = this.viewDate = '';
14875             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14876             return;
14877         }
14878         
14879         v = this.formatDate(d);
14880         
14881         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14882         
14883         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14884      
14885         this.update();
14886
14887         this.fireEvent('select', this, this.date);
14888         
14889     },
14890     
14891     getValue: function()
14892     {
14893         return this.formatDate(this.date);
14894     },
14895     
14896     fireKey: function(e)
14897     {
14898         if (!this.picker().isVisible()){
14899             if (e.keyCode == 27) // allow escape to hide and re-show picker
14900                 this.show();
14901             return;
14902         }
14903         
14904         var dateChanged = false,
14905         dir, day, month,
14906         newDate, newViewDate;
14907         
14908         switch(e.keyCode){
14909             case 27: // escape
14910                 this.hide();
14911                 e.preventDefault();
14912                 break;
14913             case 37: // left
14914             case 39: // right
14915                 if (!this.keyboardNavigation) break;
14916                 dir = e.keyCode == 37 ? -1 : 1;
14917                 
14918                 if (e.ctrlKey){
14919                     newDate = this.moveYear(this.date, dir);
14920                     newViewDate = this.moveYear(this.viewDate, dir);
14921                 } else if (e.shiftKey){
14922                     newDate = this.moveMonth(this.date, dir);
14923                     newViewDate = this.moveMonth(this.viewDate, dir);
14924                 } else {
14925                     newDate = new Date(this.date);
14926                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14927                     newViewDate = new Date(this.viewDate);
14928                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14929                 }
14930                 if (this.dateWithinRange(newDate)){
14931                     this.date = newDate;
14932                     this.viewDate = newViewDate;
14933                     this.setValue(this.formatDate(this.date));
14934 //                    this.update();
14935                     e.preventDefault();
14936                     dateChanged = true;
14937                 }
14938                 break;
14939             case 38: // up
14940             case 40: // down
14941                 if (!this.keyboardNavigation) break;
14942                 dir = e.keyCode == 38 ? -1 : 1;
14943                 if (e.ctrlKey){
14944                     newDate = this.moveYear(this.date, dir);
14945                     newViewDate = this.moveYear(this.viewDate, dir);
14946                 } else if (e.shiftKey){
14947                     newDate = this.moveMonth(this.date, dir);
14948                     newViewDate = this.moveMonth(this.viewDate, dir);
14949                 } else {
14950                     newDate = new Date(this.date);
14951                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14952                     newViewDate = new Date(this.viewDate);
14953                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14954                 }
14955                 if (this.dateWithinRange(newDate)){
14956                     this.date = newDate;
14957                     this.viewDate = newViewDate;
14958                     this.setValue(this.formatDate(this.date));
14959 //                    this.update();
14960                     e.preventDefault();
14961                     dateChanged = true;
14962                 }
14963                 break;
14964             case 13: // enter
14965                 this.setValue(this.formatDate(this.date));
14966                 this.hide();
14967                 e.preventDefault();
14968                 break;
14969             case 9: // tab
14970                 this.setValue(this.formatDate(this.date));
14971                 this.hide();
14972                 break;
14973             case 16: // shift
14974             case 17: // ctrl
14975             case 18: // alt
14976                 break;
14977             default :
14978                 this.hide();
14979                 
14980         }
14981     },
14982     
14983     
14984     onClick: function(e) 
14985     {
14986         e.stopPropagation();
14987         e.preventDefault();
14988         
14989         var target = e.getTarget();
14990         
14991         if(target.nodeName.toLowerCase() === 'i'){
14992             target = Roo.get(target).dom.parentNode;
14993         }
14994         
14995         var nodeName = target.nodeName;
14996         var className = target.className;
14997         var html = target.innerHTML;
14998         
14999         switch(nodeName.toLowerCase()) {
15000             case 'th':
15001                 switch(className) {
15002                     case 'switch':
15003                         this.showMode(1);
15004                         break;
15005                     case 'prev':
15006                     case 'next':
15007                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15008                         switch(this.viewMode){
15009                                 case 0:
15010                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15011                                         break;
15012                                 case 1:
15013                                 case 2:
15014                                         this.viewDate = this.moveYear(this.viewDate, dir);
15015                                         break;
15016                         }
15017                         this.fill();
15018                         break;
15019                     case 'today':
15020                         var date = new Date();
15021                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15022 //                        this.fill()
15023                         this.setValue(this.formatDate(this.date));
15024                         
15025                         this.hide();
15026                         break;
15027                 }
15028                 break;
15029             case 'span':
15030                 if (className.indexOf('disabled') === -1) {
15031                     this.viewDate.setUTCDate(1);
15032                     if (className.indexOf('month') !== -1) {
15033                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15034                     } else {
15035                         var year = parseInt(html, 10) || 0;
15036                         this.viewDate.setUTCFullYear(year);
15037                         
15038                     }
15039                     this.showMode(-1);
15040                     this.fill();
15041                 }
15042                 break;
15043                 
15044             case 'td':
15045                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15046                     var day = parseInt(html, 10) || 1;
15047                     var year = this.viewDate.getUTCFullYear(),
15048                         month = this.viewDate.getUTCMonth();
15049
15050                     if (className.indexOf('old') !== -1) {
15051                         if(month === 0 ){
15052                             month = 11;
15053                             year -= 1;
15054                         }else{
15055                             month -= 1;
15056                         }
15057                     } else if (className.indexOf('new') !== -1) {
15058                         if (month == 11) {
15059                             month = 0;
15060                             year += 1;
15061                         } else {
15062                             month += 1;
15063                         }
15064                     }
15065                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15066                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15067 //                    this.fill();
15068                     this.setValue(this.formatDate(this.date));
15069                     this.hide();
15070                 }
15071                 break;
15072         }
15073     },
15074     
15075     setStartDate: function(startDate)
15076     {
15077         this.startDate = startDate || -Infinity;
15078         if (this.startDate !== -Infinity) {
15079             this.startDate = this.parseDate(this.startDate);
15080         }
15081         this.update();
15082         this.updateNavArrows();
15083     },
15084
15085     setEndDate: function(endDate)
15086     {
15087         this.endDate = endDate || Infinity;
15088         if (this.endDate !== Infinity) {
15089             this.endDate = this.parseDate(this.endDate);
15090         }
15091         this.update();
15092         this.updateNavArrows();
15093     },
15094     
15095     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15096     {
15097         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15098         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15099             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15100         }
15101         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15102             return parseInt(d, 10);
15103         });
15104         this.update();
15105         this.updateNavArrows();
15106     },
15107     
15108     updateNavArrows: function() 
15109     {
15110         var d = new Date(this.viewDate),
15111         year = d.getUTCFullYear(),
15112         month = d.getUTCMonth();
15113         
15114         Roo.each(this.picker().select('.prev', true).elements, function(v){
15115             v.show();
15116             switch (this.viewMode) {
15117                 case 0:
15118
15119                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15120                         v.hide();
15121                     }
15122                     break;
15123                 case 1:
15124                 case 2:
15125                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15126                         v.hide();
15127                     }
15128                     break;
15129             }
15130         });
15131         
15132         Roo.each(this.picker().select('.next', true).elements, function(v){
15133             v.show();
15134             switch (this.viewMode) {
15135                 case 0:
15136
15137                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15138                         v.hide();
15139                     }
15140                     break;
15141                 case 1:
15142                 case 2:
15143                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15144                         v.hide();
15145                     }
15146                     break;
15147             }
15148         })
15149     },
15150     
15151     moveMonth: function(date, dir)
15152     {
15153         if (!dir) return date;
15154         var new_date = new Date(date.valueOf()),
15155         day = new_date.getUTCDate(),
15156         month = new_date.getUTCMonth(),
15157         mag = Math.abs(dir),
15158         new_month, test;
15159         dir = dir > 0 ? 1 : -1;
15160         if (mag == 1){
15161             test = dir == -1
15162             // If going back one month, make sure month is not current month
15163             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15164             ? function(){
15165                 return new_date.getUTCMonth() == month;
15166             }
15167             // If going forward one month, make sure month is as expected
15168             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15169             : function(){
15170                 return new_date.getUTCMonth() != new_month;
15171             };
15172             new_month = month + dir;
15173             new_date.setUTCMonth(new_month);
15174             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15175             if (new_month < 0 || new_month > 11)
15176                 new_month = (new_month + 12) % 12;
15177         } else {
15178             // For magnitudes >1, move one month at a time...
15179             for (var i=0; i<mag; i++)
15180                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15181                 new_date = this.moveMonth(new_date, dir);
15182             // ...then reset the day, keeping it in the new month
15183             new_month = new_date.getUTCMonth();
15184             new_date.setUTCDate(day);
15185             test = function(){
15186                 return new_month != new_date.getUTCMonth();
15187             };
15188         }
15189         // Common date-resetting loop -- if date is beyond end of month, make it
15190         // end of month
15191         while (test()){
15192             new_date.setUTCDate(--day);
15193             new_date.setUTCMonth(new_month);
15194         }
15195         return new_date;
15196     },
15197
15198     moveYear: function(date, dir)
15199     {
15200         return this.moveMonth(date, dir*12);
15201     },
15202
15203     dateWithinRange: function(date)
15204     {
15205         return date >= this.startDate && date <= this.endDate;
15206     },
15207
15208     
15209     remove: function() 
15210     {
15211         this.picker().remove();
15212     }
15213    
15214 });
15215
15216 Roo.apply(Roo.bootstrap.DateField,  {
15217     
15218     head : {
15219         tag: 'thead',
15220         cn: [
15221         {
15222             tag: 'tr',
15223             cn: [
15224             {
15225                 tag: 'th',
15226                 cls: 'prev',
15227                 html: '<i class="fa fa-arrow-left"/>'
15228             },
15229             {
15230                 tag: 'th',
15231                 cls: 'switch',
15232                 colspan: '5'
15233             },
15234             {
15235                 tag: 'th',
15236                 cls: 'next',
15237                 html: '<i class="fa fa-arrow-right"/>'
15238             }
15239
15240             ]
15241         }
15242         ]
15243     },
15244     
15245     content : {
15246         tag: 'tbody',
15247         cn: [
15248         {
15249             tag: 'tr',
15250             cn: [
15251             {
15252                 tag: 'td',
15253                 colspan: '7'
15254             }
15255             ]
15256         }
15257         ]
15258     },
15259     
15260     footer : {
15261         tag: 'tfoot',
15262         cn: [
15263         {
15264             tag: 'tr',
15265             cn: [
15266             {
15267                 tag: 'th',
15268                 colspan: '7',
15269                 cls: 'today'
15270             }
15271                     
15272             ]
15273         }
15274         ]
15275     },
15276     
15277     dates:{
15278         en: {
15279             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15280             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15281             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15282             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15283             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15284             today: "Today"
15285         }
15286     },
15287     
15288     modes: [
15289     {
15290         clsName: 'days',
15291         navFnc: 'Month',
15292         navStep: 1
15293     },
15294     {
15295         clsName: 'months',
15296         navFnc: 'FullYear',
15297         navStep: 1
15298     },
15299     {
15300         clsName: 'years',
15301         navFnc: 'FullYear',
15302         navStep: 10
15303     }]
15304 });
15305
15306 Roo.apply(Roo.bootstrap.DateField,  {
15307   
15308     template : {
15309         tag: 'div',
15310         cls: 'datepicker dropdown-menu',
15311         cn: [
15312         {
15313             tag: 'div',
15314             cls: 'datepicker-days',
15315             cn: [
15316             {
15317                 tag: 'table',
15318                 cls: 'table-condensed',
15319                 cn:[
15320                 Roo.bootstrap.DateField.head,
15321                 {
15322                     tag: 'tbody'
15323                 },
15324                 Roo.bootstrap.DateField.footer
15325                 ]
15326             }
15327             ]
15328         },
15329         {
15330             tag: 'div',
15331             cls: 'datepicker-months',
15332             cn: [
15333             {
15334                 tag: 'table',
15335                 cls: 'table-condensed',
15336                 cn:[
15337                 Roo.bootstrap.DateField.head,
15338                 Roo.bootstrap.DateField.content,
15339                 Roo.bootstrap.DateField.footer
15340                 ]
15341             }
15342             ]
15343         },
15344         {
15345             tag: 'div',
15346             cls: 'datepicker-years',
15347             cn: [
15348             {
15349                 tag: 'table',
15350                 cls: 'table-condensed',
15351                 cn:[
15352                 Roo.bootstrap.DateField.head,
15353                 Roo.bootstrap.DateField.content,
15354                 Roo.bootstrap.DateField.footer
15355                 ]
15356             }
15357             ]
15358         }
15359         ]
15360     }
15361 });
15362
15363  
15364
15365  /*
15366  * - LGPL
15367  *
15368  * TimeField
15369  * 
15370  */
15371
15372 /**
15373  * @class Roo.bootstrap.TimeField
15374  * @extends Roo.bootstrap.Input
15375  * Bootstrap DateField class
15376  * 
15377  * 
15378  * @constructor
15379  * Create a new TimeField
15380  * @param {Object} config The config object
15381  */
15382
15383 Roo.bootstrap.TimeField = function(config){
15384     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15385     this.addEvents({
15386             /**
15387              * @event show
15388              * Fires when this field show.
15389              * @param {Roo.bootstrap.DateField} this
15390              * @param {Mixed} date The date value
15391              */
15392             show : true,
15393             /**
15394              * @event show
15395              * Fires when this field hide.
15396              * @param {Roo.bootstrap.DateField} this
15397              * @param {Mixed} date The date value
15398              */
15399             hide : true,
15400             /**
15401              * @event select
15402              * Fires when select a date.
15403              * @param {Roo.bootstrap.DateField} this
15404              * @param {Mixed} date The date value
15405              */
15406             select : true
15407         });
15408 };
15409
15410 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15411     
15412     /**
15413      * @cfg {String} format
15414      * The default time format string which can be overriden for localization support.  The format must be
15415      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15416      */
15417     format : "H:i",
15418        
15419     onRender: function(ct, position)
15420     {
15421         
15422         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15423                 
15424         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15425         
15426         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15427         
15428         this.pop = this.picker().select('>.datepicker-time',true).first();
15429         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15430         
15431         this.picker().on('mousedown', this.onMousedown, this);
15432         this.picker().on('click', this.onClick, this);
15433         
15434         this.picker().addClass('datepicker-dropdown');
15435     
15436         this.fillTime();
15437         this.update();
15438             
15439         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15440         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15441         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15442         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15443         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15444         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15445
15446     },
15447     
15448     fireKey: function(e){
15449         if (!this.picker().isVisible()){
15450             if (e.keyCode == 27) // allow escape to hide and re-show picker
15451                 this.show();
15452             return;
15453         }
15454
15455         e.preventDefault();
15456         
15457         switch(e.keyCode){
15458             case 27: // escape
15459                 this.hide();
15460                 break;
15461             case 37: // left
15462             case 39: // right
15463                 this.onTogglePeriod();
15464                 break;
15465             case 38: // up
15466                 this.onIncrementMinutes();
15467                 break;
15468             case 40: // down
15469                 this.onDecrementMinutes();
15470                 break;
15471             case 13: // enter
15472             case 9: // tab
15473                 this.setTime();
15474                 break;
15475         }
15476     },
15477     
15478     onClick: function(e) {
15479         e.stopPropagation();
15480         e.preventDefault();
15481     },
15482     
15483     picker : function()
15484     {
15485         return this.el.select('.datepicker', true).first();
15486     },
15487     
15488     fillTime: function()
15489     {    
15490         var time = this.pop.select('tbody', true).first();
15491         
15492         time.dom.innerHTML = '';
15493         
15494         time.createChild({
15495             tag: 'tr',
15496             cn: [
15497                 {
15498                     tag: 'td',
15499                     cn: [
15500                         {
15501                             tag: 'a',
15502                             href: '#',
15503                             cls: 'btn',
15504                             cn: [
15505                                 {
15506                                     tag: 'span',
15507                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15508                                 }
15509                             ]
15510                         } 
15511                     ]
15512                 },
15513                 {
15514                     tag: 'td',
15515                     cls: 'separator'
15516                 },
15517                 {
15518                     tag: 'td',
15519                     cn: [
15520                         {
15521                             tag: 'a',
15522                             href: '#',
15523                             cls: 'btn',
15524                             cn: [
15525                                 {
15526                                     tag: 'span',
15527                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15528                                 }
15529                             ]
15530                         }
15531                     ]
15532                 },
15533                 {
15534                     tag: 'td',
15535                     cls: 'separator'
15536                 }
15537             ]
15538         });
15539         
15540         time.createChild({
15541             tag: 'tr',
15542             cn: [
15543                 {
15544                     tag: 'td',
15545                     cn: [
15546                         {
15547                             tag: 'span',
15548                             cls: 'timepicker-hour',
15549                             html: '00'
15550                         }  
15551                     ]
15552                 },
15553                 {
15554                     tag: 'td',
15555                     cls: 'separator',
15556                     html: ':'
15557                 },
15558                 {
15559                     tag: 'td',
15560                     cn: [
15561                         {
15562                             tag: 'span',
15563                             cls: 'timepicker-minute',
15564                             html: '00'
15565                         }  
15566                     ]
15567                 },
15568                 {
15569                     tag: 'td',
15570                     cls: 'separator'
15571                 },
15572                 {
15573                     tag: 'td',
15574                     cn: [
15575                         {
15576                             tag: 'button',
15577                             type: 'button',
15578                             cls: 'btn btn-primary period',
15579                             html: 'AM'
15580                             
15581                         }
15582                     ]
15583                 }
15584             ]
15585         });
15586         
15587         time.createChild({
15588             tag: 'tr',
15589             cn: [
15590                 {
15591                     tag: 'td',
15592                     cn: [
15593                         {
15594                             tag: 'a',
15595                             href: '#',
15596                             cls: 'btn',
15597                             cn: [
15598                                 {
15599                                     tag: 'span',
15600                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15601                                 }
15602                             ]
15603                         }
15604                     ]
15605                 },
15606                 {
15607                     tag: 'td',
15608                     cls: 'separator'
15609                 },
15610                 {
15611                     tag: 'td',
15612                     cn: [
15613                         {
15614                             tag: 'a',
15615                             href: '#',
15616                             cls: 'btn',
15617                             cn: [
15618                                 {
15619                                     tag: 'span',
15620                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15621                                 }
15622                             ]
15623                         }
15624                     ]
15625                 },
15626                 {
15627                     tag: 'td',
15628                     cls: 'separator'
15629                 }
15630             ]
15631         });
15632         
15633     },
15634     
15635     update: function()
15636     {
15637         
15638         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15639         
15640         this.fill();
15641     },
15642     
15643     fill: function() 
15644     {
15645         var hours = this.time.getHours();
15646         var minutes = this.time.getMinutes();
15647         var period = 'AM';
15648         
15649         if(hours > 11){
15650             period = 'PM';
15651         }
15652         
15653         if(hours == 0){
15654             hours = 12;
15655         }
15656         
15657         
15658         if(hours > 12){
15659             hours = hours - 12;
15660         }
15661         
15662         if(hours < 10){
15663             hours = '0' + hours;
15664         }
15665         
15666         if(minutes < 10){
15667             minutes = '0' + minutes;
15668         }
15669         
15670         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15671         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15672         this.pop.select('button', true).first().dom.innerHTML = period;
15673         
15674     },
15675     
15676     place: function()
15677     {   
15678         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15679         
15680         var cls = ['bottom'];
15681         
15682         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15683             cls.pop();
15684             cls.push('top');
15685         }
15686         
15687         cls.push('right');
15688         
15689         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15690             cls.pop();
15691             cls.push('left');
15692         }
15693         
15694         this.picker().addClass(cls.join('-'));
15695         
15696         var _this = this;
15697         
15698         Roo.each(cls, function(c){
15699             if(c == 'bottom'){
15700                 _this.picker().setTop(_this.inputEl().getHeight());
15701                 return;
15702             }
15703             if(c == 'top'){
15704                 _this.picker().setTop(0 - _this.picker().getHeight());
15705                 return;
15706             }
15707             
15708             if(c == 'left'){
15709                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15710                 return;
15711             }
15712             if(c == 'right'){
15713                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15714                 return;
15715             }
15716         });
15717         
15718     },
15719   
15720     onFocus : function()
15721     {
15722         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15723         this.show();
15724     },
15725     
15726     onBlur : function()
15727     {
15728         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15729         this.hide();
15730     },
15731     
15732     show : function()
15733     {
15734         this.picker().show();
15735         this.pop.show();
15736         this.update();
15737         this.place();
15738         
15739         this.fireEvent('show', this, this.date);
15740     },
15741     
15742     hide : function()
15743     {
15744         this.picker().hide();
15745         this.pop.hide();
15746         
15747         this.fireEvent('hide', this, this.date);
15748     },
15749     
15750     setTime : function()
15751     {
15752         this.hide();
15753         this.setValue(this.time.format(this.format));
15754         
15755         this.fireEvent('select', this, this.date);
15756         
15757         
15758     },
15759     
15760     onMousedown: function(e){
15761         e.stopPropagation();
15762         e.preventDefault();
15763     },
15764     
15765     onIncrementHours: function()
15766     {
15767         Roo.log('onIncrementHours');
15768         this.time = this.time.add(Date.HOUR, 1);
15769         this.update();
15770         
15771     },
15772     
15773     onDecrementHours: function()
15774     {
15775         Roo.log('onDecrementHours');
15776         this.time = this.time.add(Date.HOUR, -1);
15777         this.update();
15778     },
15779     
15780     onIncrementMinutes: function()
15781     {
15782         Roo.log('onIncrementMinutes');
15783         this.time = this.time.add(Date.MINUTE, 1);
15784         this.update();
15785     },
15786     
15787     onDecrementMinutes: function()
15788     {
15789         Roo.log('onDecrementMinutes');
15790         this.time = this.time.add(Date.MINUTE, -1);
15791         this.update();
15792     },
15793     
15794     onTogglePeriod: function()
15795     {
15796         Roo.log('onTogglePeriod');
15797         this.time = this.time.add(Date.HOUR, 12);
15798         this.update();
15799     }
15800     
15801    
15802 });
15803
15804 Roo.apply(Roo.bootstrap.TimeField,  {
15805     
15806     content : {
15807         tag: 'tbody',
15808         cn: [
15809             {
15810                 tag: 'tr',
15811                 cn: [
15812                 {
15813                     tag: 'td',
15814                     colspan: '7'
15815                 }
15816                 ]
15817             }
15818         ]
15819     },
15820     
15821     footer : {
15822         tag: 'tfoot',
15823         cn: [
15824             {
15825                 tag: 'tr',
15826                 cn: [
15827                 {
15828                     tag: 'th',
15829                     colspan: '7',
15830                     cls: '',
15831                     cn: [
15832                         {
15833                             tag: 'button',
15834                             cls: 'btn btn-info ok',
15835                             html: 'OK'
15836                         }
15837                     ]
15838                 }
15839
15840                 ]
15841             }
15842         ]
15843     }
15844 });
15845
15846 Roo.apply(Roo.bootstrap.TimeField,  {
15847   
15848     template : {
15849         tag: 'div',
15850         cls: 'datepicker dropdown-menu',
15851         cn: [
15852             {
15853                 tag: 'div',
15854                 cls: 'datepicker-time',
15855                 cn: [
15856                 {
15857                     tag: 'table',
15858                     cls: 'table-condensed',
15859                     cn:[
15860                     Roo.bootstrap.TimeField.content,
15861                     Roo.bootstrap.TimeField.footer
15862                     ]
15863                 }
15864                 ]
15865             }
15866         ]
15867     }
15868 });
15869
15870  
15871
15872  /*
15873  * - LGPL
15874  *
15875  * CheckBox
15876  * 
15877  */
15878
15879 /**
15880  * @class Roo.bootstrap.CheckBox
15881  * @extends Roo.bootstrap.Input
15882  * Bootstrap CheckBox class
15883  * 
15884  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15885  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15886  * @cfg {String} boxLabel The text that appears beside the checkbox
15887  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15888  * @cfg {Boolean} checked initnal the element
15889  * 
15890  * 
15891  * @constructor
15892  * Create a new CheckBox
15893  * @param {Object} config The config object
15894  */
15895
15896 Roo.bootstrap.CheckBox = function(config){
15897     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15898    
15899         this.addEvents({
15900             /**
15901             * @event check
15902             * Fires when the element is checked or unchecked.
15903             * @param {Roo.bootstrap.CheckBox} this This input
15904             * @param {Boolean} checked The new checked value
15905             */
15906            check : true
15907         });
15908 };
15909
15910 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15911     
15912     inputType: 'checkbox',
15913     inputValue: 1,
15914     valueOff: 0,
15915     boxLabel: false,
15916     checked: false,
15917     weight : false,
15918     
15919     getAutoCreate : function()
15920     {
15921         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15922         
15923         var id = Roo.id();
15924         
15925         var cfg = {};
15926         
15927         cfg.cls = 'form-group checkbox' //input-group
15928         
15929         
15930         
15931         
15932         var input =  {
15933             tag: 'input',
15934             id : id,
15935             type : this.inputType,
15936             value : (!this.checked) ? this.valueOff : this.inputValue,
15937             cls : 'roo-checkbox', //'form-box',
15938             placeholder : this.placeholder || ''
15939             
15940         };
15941         
15942         if (this.weight) { // Validity check?
15943             cfg.cls += " checkbox-" + this.weight;
15944         }
15945         
15946         if (this.disabled) {
15947             input.disabled=true;
15948         }
15949         
15950         if(this.checked){
15951             input.checked = this.checked;
15952         }
15953         
15954         if (this.name) {
15955             input.name = this.name;
15956         }
15957         
15958         if (this.size) {
15959             input.cls += ' input-' + this.size;
15960         }
15961         
15962         var settings=this;
15963         ['xs','sm','md','lg'].map(function(size){
15964             if (settings[size]) {
15965                 cfg.cls += ' col-' + size + '-' + settings[size];
15966             }
15967         });
15968         
15969        
15970         
15971         var inputblock = input;
15972         
15973         
15974         
15975         
15976         if (this.before || this.after) {
15977             
15978             inputblock = {
15979                 cls : 'input-group',
15980                 cn :  [] 
15981             };
15982             if (this.before) {
15983                 inputblock.cn.push({
15984                     tag :'span',
15985                     cls : 'input-group-addon',
15986                     html : this.before
15987                 });
15988             }
15989             inputblock.cn.push(input);
15990             if (this.after) {
15991                 inputblock.cn.push({
15992                     tag :'span',
15993                     cls : 'input-group-addon',
15994                     html : this.after
15995                 });
15996             }
15997             
15998         };
15999         
16000         if (align ==='left' && this.fieldLabel.length) {
16001                 Roo.log("left and has label");
16002                 cfg.cn = [
16003                     
16004                     {
16005                         tag: 'label',
16006                         'for' :  id,
16007                         cls : 'control-label col-md-' + this.labelWidth,
16008                         html : this.fieldLabel
16009                         
16010                     },
16011                     {
16012                         cls : "col-md-" + (12 - this.labelWidth), 
16013                         cn: [
16014                             inputblock
16015                         ]
16016                     }
16017                     
16018                 ];
16019         } else if ( this.fieldLabel.length) {
16020                 Roo.log(" label");
16021                 cfg.cn = [
16022                    
16023                     {
16024                         tag: this.boxLabel ? 'span' : 'label',
16025                         'for': id,
16026                         cls: 'control-label box-input-label',
16027                         //cls : 'input-group-addon',
16028                         html : this.fieldLabel
16029                         
16030                     },
16031                     
16032                     inputblock
16033                     
16034                 ];
16035
16036         } else {
16037             
16038                 Roo.log(" no label && no align");
16039                 cfg.cn = [  inputblock ] ;
16040                 
16041                 
16042         };
16043          if(this.boxLabel){
16044             cfg.cn.push( {
16045                 tag: 'label',
16046                 'for': id,
16047                 cls: 'box-label',
16048                 html: this.boxLabel
16049                 
16050             });
16051         }
16052         
16053         
16054        
16055         return cfg;
16056         
16057     },
16058     
16059     /**
16060      * return the real input element.
16061      */
16062     inputEl: function ()
16063     {
16064         return this.el.select('input.roo-checkbox',true).first();
16065     },
16066     
16067     label: function()
16068     {
16069         return this.el.select('label.control-label',true).first();
16070     },
16071     
16072     initEvents : function()
16073     {
16074 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16075         
16076         this.inputEl().on('click', this.onClick,  this);
16077         
16078     },
16079     
16080     onClick : function()
16081     {   
16082         this.setChecked(!this.checked);
16083     },
16084     
16085     setChecked : function(state,suppressEvent)
16086     {
16087         this.checked = state;
16088         
16089         this.inputEl().dom.checked = state;
16090         
16091         if(suppressEvent !== true){
16092             this.fireEvent('check', this, state);
16093         }
16094         
16095         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16096         
16097     },
16098     
16099     setValue : function(v,suppressEvent)
16100     {
16101         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16102     }
16103     
16104 });
16105
16106  
16107 /*
16108  * - LGPL
16109  *
16110  * Radio
16111  * 
16112  */
16113
16114 /**
16115  * @class Roo.bootstrap.Radio
16116  * @extends Roo.bootstrap.CheckBox
16117  * Bootstrap Radio class
16118
16119  * @constructor
16120  * Create a new Radio
16121  * @param {Object} config The config object
16122  */
16123
16124 Roo.bootstrap.Radio = function(config){
16125     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16126    
16127 };
16128
16129 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16130     
16131     inputType: 'radio',
16132     inputValue: '',
16133     valueOff: '',
16134     
16135     getAutoCreate : function()
16136     {
16137         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16138         
16139         var id = Roo.id();
16140         
16141         var cfg = {};
16142         
16143         cfg.cls = 'form-group radio' //input-group
16144         
16145         var input =  {
16146             tag: 'input',
16147             id : id,
16148             type : this.inputType,
16149             value : (!this.checked) ? this.valueOff : this.inputValue,
16150             cls : 'roo-radio',
16151             placeholder : this.placeholder || ''
16152             
16153         };
16154           if (this.weight) { // Validity check?
16155             cfg.cls += " radio-" + this.weight;
16156         }
16157         if (this.disabled) {
16158             input.disabled=true;
16159         }
16160         
16161         if(this.checked){
16162             input.checked = this.checked;
16163         }
16164         
16165         if (this.name) {
16166             input.name = this.name;
16167         }
16168         
16169         if (this.size) {
16170             input.cls += ' input-' + this.size;
16171         }
16172         
16173         var settings=this;
16174         ['xs','sm','md','lg'].map(function(size){
16175             if (settings[size]) {
16176                 cfg.cls += ' col-' + size + '-' + settings[size];
16177             }
16178         });
16179         
16180         var inputblock = input;
16181         
16182         if (this.before || this.after) {
16183             
16184             inputblock = {
16185                 cls : 'input-group',
16186                 cn :  [] 
16187             };
16188             if (this.before) {
16189                 inputblock.cn.push({
16190                     tag :'span',
16191                     cls : 'input-group-addon',
16192                     html : this.before
16193                 });
16194             }
16195             inputblock.cn.push(input);
16196             if (this.after) {
16197                 inputblock.cn.push({
16198                     tag :'span',
16199                     cls : 'input-group-addon',
16200                     html : this.after
16201                 });
16202             }
16203             
16204         };
16205         
16206         if (align ==='left' && this.fieldLabel.length) {
16207                 Roo.log("left and has label");
16208                 cfg.cn = [
16209                     
16210                     {
16211                         tag: 'label',
16212                         'for' :  id,
16213                         cls : 'control-label col-md-' + this.labelWidth,
16214                         html : this.fieldLabel
16215                         
16216                     },
16217                     {
16218                         cls : "col-md-" + (12 - this.labelWidth), 
16219                         cn: [
16220                             inputblock
16221                         ]
16222                     }
16223                     
16224                 ];
16225         } else if ( this.fieldLabel.length) {
16226                 Roo.log(" label");
16227                  cfg.cn = [
16228                    
16229                     {
16230                         tag: 'label',
16231                         'for': id,
16232                         cls: 'control-label box-input-label',
16233                         //cls : 'input-group-addon',
16234                         html : this.fieldLabel
16235                         
16236                     },
16237                     
16238                     inputblock
16239                     
16240                 ];
16241
16242         } else {
16243             
16244                    Roo.log(" no label && no align");
16245                 cfg.cn = [
16246                     
16247                         inputblock
16248                     
16249                 ];
16250                 
16251                 
16252         };
16253         
16254         if(this.boxLabel){
16255             cfg.cn.push({
16256                 tag: 'label',
16257                 'for': id,
16258                 cls: 'box-label',
16259                 html: this.boxLabel
16260             })
16261         }
16262         
16263         return cfg;
16264         
16265     },
16266     inputEl: function ()
16267     {
16268         return this.el.select('input.roo-radio',true).first();
16269     },
16270     onClick : function()
16271     {   
16272         this.setChecked(true);
16273     },
16274     
16275     setChecked : function(state,suppressEvent)
16276     {
16277         if(state){
16278             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16279                 v.dom.checked = false;
16280             });
16281         }
16282         
16283         this.checked = state;
16284         this.inputEl().dom.checked = state;
16285         
16286         if(suppressEvent !== true){
16287             this.fireEvent('check', this, state);
16288         }
16289         
16290         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16291         
16292     },
16293     
16294     getGroupValue : function()
16295     {
16296         var value = ''
16297         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16298             if(v.dom.checked == true){
16299                 value = v.dom.value;
16300             }
16301         });
16302         
16303         return value;
16304     },
16305     
16306     /**
16307      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16308      * @return {Mixed} value The field value
16309      */
16310     getValue : function(){
16311         return this.getGroupValue();
16312     }
16313     
16314 });
16315
16316  
16317 //<script type="text/javascript">
16318
16319 /*
16320  * Based  Ext JS Library 1.1.1
16321  * Copyright(c) 2006-2007, Ext JS, LLC.
16322  * LGPL
16323  *
16324  */
16325  
16326 /**
16327  * @class Roo.HtmlEditorCore
16328  * @extends Roo.Component
16329  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16330  *
16331  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16332  */
16333
16334 Roo.HtmlEditorCore = function(config){
16335     
16336     
16337     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16338     
16339     
16340     this.addEvents({
16341         /**
16342          * @event initialize
16343          * Fires when the editor is fully initialized (including the iframe)
16344          * @param {Roo.HtmlEditorCore} this
16345          */
16346         initialize: true,
16347         /**
16348          * @event activate
16349          * Fires when the editor is first receives the focus. Any insertion must wait
16350          * until after this event.
16351          * @param {Roo.HtmlEditorCore} this
16352          */
16353         activate: true,
16354          /**
16355          * @event beforesync
16356          * Fires before the textarea is updated with content from the editor iframe. Return false
16357          * to cancel the sync.
16358          * @param {Roo.HtmlEditorCore} this
16359          * @param {String} html
16360          */
16361         beforesync: true,
16362          /**
16363          * @event beforepush
16364          * Fires before the iframe editor is updated with content from the textarea. Return false
16365          * to cancel the push.
16366          * @param {Roo.HtmlEditorCore} this
16367          * @param {String} html
16368          */
16369         beforepush: true,
16370          /**
16371          * @event sync
16372          * Fires when the textarea is updated with content from the editor iframe.
16373          * @param {Roo.HtmlEditorCore} this
16374          * @param {String} html
16375          */
16376         sync: true,
16377          /**
16378          * @event push
16379          * Fires when the iframe editor is updated with content from the textarea.
16380          * @param {Roo.HtmlEditorCore} this
16381          * @param {String} html
16382          */
16383         push: true,
16384         
16385         /**
16386          * @event editorevent
16387          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16388          * @param {Roo.HtmlEditorCore} this
16389          */
16390         editorevent: true
16391     });
16392     
16393     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16394     
16395     // defaults : white / black...
16396     this.applyBlacklists();
16397     
16398     
16399     
16400 };
16401
16402
16403 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16404
16405
16406      /**
16407      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16408      */
16409     
16410     owner : false,
16411     
16412      /**
16413      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16414      *                        Roo.resizable.
16415      */
16416     resizable : false,
16417      /**
16418      * @cfg {Number} height (in pixels)
16419      */   
16420     height: 300,
16421    /**
16422      * @cfg {Number} width (in pixels)
16423      */   
16424     width: 500,
16425     
16426     /**
16427      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16428      * 
16429      */
16430     stylesheets: false,
16431     
16432     // id of frame..
16433     frameId: false,
16434     
16435     // private properties
16436     validationEvent : false,
16437     deferHeight: true,
16438     initialized : false,
16439     activated : false,
16440     sourceEditMode : false,
16441     onFocus : Roo.emptyFn,
16442     iframePad:3,
16443     hideMode:'offsets',
16444     
16445     clearUp: true,
16446     
16447     // blacklist + whitelisted elements..
16448     black: false,
16449     white: false,
16450      
16451     
16452
16453     /**
16454      * Protected method that will not generally be called directly. It
16455      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16456      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16457      */
16458     getDocMarkup : function(){
16459         // body styles..
16460         var st = '';
16461         Roo.log(this.stylesheets);
16462         
16463         // inherit styels from page...?? 
16464         if (this.stylesheets === false) {
16465             
16466             Roo.get(document.head).select('style').each(function(node) {
16467                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16468             });
16469             
16470             Roo.get(document.head).select('link').each(function(node) { 
16471                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16472             });
16473             
16474         } else if (!this.stylesheets.length) {
16475                 // simple..
16476                 st = '<style type="text/css">' +
16477                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16478                    '</style>';
16479         } else {
16480             Roo.each(this.stylesheets, function(s) {
16481                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16482             });
16483             
16484         }
16485         
16486         st +=  '<style type="text/css">' +
16487             'IMG { cursor: pointer } ' +
16488         '</style>';
16489
16490         
16491         return '<html><head>' + st  +
16492             //<style type="text/css">' +
16493             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16494             //'</style>' +
16495             ' </head><body class="roo-htmleditor-body"></body></html>';
16496     },
16497
16498     // private
16499     onRender : function(ct, position)
16500     {
16501         var _t = this;
16502         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16503         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16504         
16505         
16506         this.el.dom.style.border = '0 none';
16507         this.el.dom.setAttribute('tabIndex', -1);
16508         this.el.addClass('x-hidden hide');
16509         
16510         
16511         
16512         if(Roo.isIE){ // fix IE 1px bogus margin
16513             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16514         }
16515        
16516         
16517         this.frameId = Roo.id();
16518         
16519          
16520         
16521         var iframe = this.owner.wrap.createChild({
16522             tag: 'iframe',
16523             cls: 'form-control', // bootstrap..
16524             id: this.frameId,
16525             name: this.frameId,
16526             frameBorder : 'no',
16527             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16528         }, this.el
16529         );
16530         
16531         
16532         this.iframe = iframe.dom;
16533
16534          this.assignDocWin();
16535         
16536         this.doc.designMode = 'on';
16537        
16538         this.doc.open();
16539         this.doc.write(this.getDocMarkup());
16540         this.doc.close();
16541
16542         
16543         var task = { // must defer to wait for browser to be ready
16544             run : function(){
16545                 //console.log("run task?" + this.doc.readyState);
16546                 this.assignDocWin();
16547                 if(this.doc.body || this.doc.readyState == 'complete'){
16548                     try {
16549                         this.doc.designMode="on";
16550                     } catch (e) {
16551                         return;
16552                     }
16553                     Roo.TaskMgr.stop(task);
16554                     this.initEditor.defer(10, this);
16555                 }
16556             },
16557             interval : 10,
16558             duration: 10000,
16559             scope: this
16560         };
16561         Roo.TaskMgr.start(task);
16562
16563         
16564          
16565     },
16566
16567     // private
16568     onResize : function(w, h)
16569     {
16570          Roo.log('resize: ' +w + ',' + h );
16571         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16572         if(!this.iframe){
16573             return;
16574         }
16575         if(typeof w == 'number'){
16576             
16577             this.iframe.style.width = w + 'px';
16578         }
16579         if(typeof h == 'number'){
16580             
16581             this.iframe.style.height = h + 'px';
16582             if(this.doc){
16583                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16584             }
16585         }
16586         
16587     },
16588
16589     /**
16590      * Toggles the editor between standard and source edit mode.
16591      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16592      */
16593     toggleSourceEdit : function(sourceEditMode){
16594         
16595         this.sourceEditMode = sourceEditMode === true;
16596         
16597         if(this.sourceEditMode){
16598  
16599             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16600             
16601         }else{
16602             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16603             //this.iframe.className = '';
16604             this.deferFocus();
16605         }
16606         //this.setSize(this.owner.wrap.getSize());
16607         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16608     },
16609
16610     
16611   
16612
16613     /**
16614      * Protected method that will not generally be called directly. If you need/want
16615      * custom HTML cleanup, this is the method you should override.
16616      * @param {String} html The HTML to be cleaned
16617      * return {String} The cleaned HTML
16618      */
16619     cleanHtml : function(html){
16620         html = String(html);
16621         if(html.length > 5){
16622             if(Roo.isSafari){ // strip safari nonsense
16623                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16624             }
16625         }
16626         if(html == '&nbsp;'){
16627             html = '';
16628         }
16629         return html;
16630     },
16631
16632     /**
16633      * HTML Editor -> Textarea
16634      * Protected method that will not generally be called directly. Syncs the contents
16635      * of the editor iframe with the textarea.
16636      */
16637     syncValue : function(){
16638         if(this.initialized){
16639             var bd = (this.doc.body || this.doc.documentElement);
16640             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16641             var html = bd.innerHTML;
16642             if(Roo.isSafari){
16643                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16644                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16645                 if(m && m[1]){
16646                     html = '<div style="'+m[0]+'">' + html + '</div>';
16647                 }
16648             }
16649             html = this.cleanHtml(html);
16650             // fix up the special chars.. normaly like back quotes in word...
16651             // however we do not want to do this with chinese..
16652             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16653                 var cc = b.charCodeAt();
16654                 if (
16655                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16656                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16657                     (cc >= 0xf900 && cc < 0xfb00 )
16658                 ) {
16659                         return b;
16660                 }
16661                 return "&#"+cc+";" 
16662             });
16663             if(this.owner.fireEvent('beforesync', this, html) !== false){
16664                 this.el.dom.value = html;
16665                 this.owner.fireEvent('sync', this, html);
16666             }
16667         }
16668     },
16669
16670     /**
16671      * Protected method that will not generally be called directly. Pushes the value of the textarea
16672      * into the iframe editor.
16673      */
16674     pushValue : function(){
16675         if(this.initialized){
16676             var v = this.el.dom.value.trim();
16677             
16678 //            if(v.length < 1){
16679 //                v = '&#160;';
16680 //            }
16681             
16682             if(this.owner.fireEvent('beforepush', this, v) !== false){
16683                 var d = (this.doc.body || this.doc.documentElement);
16684                 d.innerHTML = v;
16685                 this.cleanUpPaste();
16686                 this.el.dom.value = d.innerHTML;
16687                 this.owner.fireEvent('push', this, v);
16688             }
16689         }
16690     },
16691
16692     // private
16693     deferFocus : function(){
16694         this.focus.defer(10, this);
16695     },
16696
16697     // doc'ed in Field
16698     focus : function(){
16699         if(this.win && !this.sourceEditMode){
16700             this.win.focus();
16701         }else{
16702             this.el.focus();
16703         }
16704     },
16705     
16706     assignDocWin: function()
16707     {
16708         var iframe = this.iframe;
16709         
16710          if(Roo.isIE){
16711             this.doc = iframe.contentWindow.document;
16712             this.win = iframe.contentWindow;
16713         } else {
16714 //            if (!Roo.get(this.frameId)) {
16715 //                return;
16716 //            }
16717 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16718 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16719             
16720             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16721                 return;
16722             }
16723             
16724             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16725             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16726         }
16727     },
16728     
16729     // private
16730     initEditor : function(){
16731         //console.log("INIT EDITOR");
16732         this.assignDocWin();
16733         
16734         
16735         
16736         this.doc.designMode="on";
16737         this.doc.open();
16738         this.doc.write(this.getDocMarkup());
16739         this.doc.close();
16740         
16741         var dbody = (this.doc.body || this.doc.documentElement);
16742         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16743         // this copies styles from the containing element into thsi one..
16744         // not sure why we need all of this..
16745         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16746         
16747         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16748         //ss['background-attachment'] = 'fixed'; // w3c
16749         dbody.bgProperties = 'fixed'; // ie
16750         //Roo.DomHelper.applyStyles(dbody, ss);
16751         Roo.EventManager.on(this.doc, {
16752             //'mousedown': this.onEditorEvent,
16753             'mouseup': this.onEditorEvent,
16754             'dblclick': this.onEditorEvent,
16755             'click': this.onEditorEvent,
16756             'keyup': this.onEditorEvent,
16757             buffer:100,
16758             scope: this
16759         });
16760         if(Roo.isGecko){
16761             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16762         }
16763         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16764             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16765         }
16766         this.initialized = true;
16767
16768         this.owner.fireEvent('initialize', this);
16769         this.pushValue();
16770     },
16771
16772     // private
16773     onDestroy : function(){
16774         
16775         
16776         
16777         if(this.rendered){
16778             
16779             //for (var i =0; i < this.toolbars.length;i++) {
16780             //    // fixme - ask toolbars for heights?
16781             //    this.toolbars[i].onDestroy();
16782            // }
16783             
16784             //this.wrap.dom.innerHTML = '';
16785             //this.wrap.remove();
16786         }
16787     },
16788
16789     // private
16790     onFirstFocus : function(){
16791         
16792         this.assignDocWin();
16793         
16794         
16795         this.activated = true;
16796          
16797     
16798         if(Roo.isGecko){ // prevent silly gecko errors
16799             this.win.focus();
16800             var s = this.win.getSelection();
16801             if(!s.focusNode || s.focusNode.nodeType != 3){
16802                 var r = s.getRangeAt(0);
16803                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16804                 r.collapse(true);
16805                 this.deferFocus();
16806             }
16807             try{
16808                 this.execCmd('useCSS', true);
16809                 this.execCmd('styleWithCSS', false);
16810             }catch(e){}
16811         }
16812         this.owner.fireEvent('activate', this);
16813     },
16814
16815     // private
16816     adjustFont: function(btn){
16817         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16818         //if(Roo.isSafari){ // safari
16819         //    adjust *= 2;
16820        // }
16821         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16822         if(Roo.isSafari){ // safari
16823             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16824             v =  (v < 10) ? 10 : v;
16825             v =  (v > 48) ? 48 : v;
16826             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16827             
16828         }
16829         
16830         
16831         v = Math.max(1, v+adjust);
16832         
16833         this.execCmd('FontSize', v  );
16834     },
16835
16836     onEditorEvent : function(e){
16837         this.owner.fireEvent('editorevent', this, e);
16838       //  this.updateToolbar();
16839         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16840     },
16841
16842     insertTag : function(tg)
16843     {
16844         // could be a bit smarter... -> wrap the current selected tRoo..
16845         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16846             
16847             range = this.createRange(this.getSelection());
16848             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16849             wrappingNode.appendChild(range.extractContents());
16850             range.insertNode(wrappingNode);
16851
16852             return;
16853             
16854             
16855             
16856         }
16857         this.execCmd("formatblock",   tg);
16858         
16859     },
16860     
16861     insertText : function(txt)
16862     {
16863         
16864         
16865         var range = this.createRange();
16866         range.deleteContents();
16867                //alert(Sender.getAttribute('label'));
16868                
16869         range.insertNode(this.doc.createTextNode(txt));
16870     } ,
16871     
16872      
16873
16874     /**
16875      * Executes a Midas editor command on the editor document and performs necessary focus and
16876      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16877      * @param {String} cmd The Midas command
16878      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16879      */
16880     relayCmd : function(cmd, value){
16881         this.win.focus();
16882         this.execCmd(cmd, value);
16883         this.owner.fireEvent('editorevent', this);
16884         //this.updateToolbar();
16885         this.owner.deferFocus();
16886     },
16887
16888     /**
16889      * Executes a Midas editor command directly on the editor document.
16890      * For visual commands, you should use {@link #relayCmd} instead.
16891      * <b>This should only be called after the editor is initialized.</b>
16892      * @param {String} cmd The Midas command
16893      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16894      */
16895     execCmd : function(cmd, value){
16896         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16897         this.syncValue();
16898     },
16899  
16900  
16901    
16902     /**
16903      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16904      * to insert tRoo.
16905      * @param {String} text | dom node.. 
16906      */
16907     insertAtCursor : function(text)
16908     {
16909         
16910         
16911         
16912         if(!this.activated){
16913             return;
16914         }
16915         /*
16916         if(Roo.isIE){
16917             this.win.focus();
16918             var r = this.doc.selection.createRange();
16919             if(r){
16920                 r.collapse(true);
16921                 r.pasteHTML(text);
16922                 this.syncValue();
16923                 this.deferFocus();
16924             
16925             }
16926             return;
16927         }
16928         */
16929         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16930             this.win.focus();
16931             
16932             
16933             // from jquery ui (MIT licenced)
16934             var range, node;
16935             var win = this.win;
16936             
16937             if (win.getSelection && win.getSelection().getRangeAt) {
16938                 range = win.getSelection().getRangeAt(0);
16939                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16940                 range.insertNode(node);
16941             } else if (win.document.selection && win.document.selection.createRange) {
16942                 // no firefox support
16943                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16944                 win.document.selection.createRange().pasteHTML(txt);
16945             } else {
16946                 // no firefox support
16947                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16948                 this.execCmd('InsertHTML', txt);
16949             } 
16950             
16951             this.syncValue();
16952             
16953             this.deferFocus();
16954         }
16955     },
16956  // private
16957     mozKeyPress : function(e){
16958         if(e.ctrlKey){
16959             var c = e.getCharCode(), cmd;
16960           
16961             if(c > 0){
16962                 c = String.fromCharCode(c).toLowerCase();
16963                 switch(c){
16964                     case 'b':
16965                         cmd = 'bold';
16966                         break;
16967                     case 'i':
16968                         cmd = 'italic';
16969                         break;
16970                     
16971                     case 'u':
16972                         cmd = 'underline';
16973                         break;
16974                     
16975                     case 'v':
16976                         this.cleanUpPaste.defer(100, this);
16977                         return;
16978                         
16979                 }
16980                 if(cmd){
16981                     this.win.focus();
16982                     this.execCmd(cmd);
16983                     this.deferFocus();
16984                     e.preventDefault();
16985                 }
16986                 
16987             }
16988         }
16989     },
16990
16991     // private
16992     fixKeys : function(){ // load time branching for fastest keydown performance
16993         if(Roo.isIE){
16994             return function(e){
16995                 var k = e.getKey(), r;
16996                 if(k == e.TAB){
16997                     e.stopEvent();
16998                     r = this.doc.selection.createRange();
16999                     if(r){
17000                         r.collapse(true);
17001                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17002                         this.deferFocus();
17003                     }
17004                     return;
17005                 }
17006                 
17007                 if(k == e.ENTER){
17008                     r = this.doc.selection.createRange();
17009                     if(r){
17010                         var target = r.parentElement();
17011                         if(!target || target.tagName.toLowerCase() != 'li'){
17012                             e.stopEvent();
17013                             r.pasteHTML('<br />');
17014                             r.collapse(false);
17015                             r.select();
17016                         }
17017                     }
17018                 }
17019                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17020                     this.cleanUpPaste.defer(100, this);
17021                     return;
17022                 }
17023                 
17024                 
17025             };
17026         }else if(Roo.isOpera){
17027             return function(e){
17028                 var k = e.getKey();
17029                 if(k == e.TAB){
17030                     e.stopEvent();
17031                     this.win.focus();
17032                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17033                     this.deferFocus();
17034                 }
17035                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17036                     this.cleanUpPaste.defer(100, this);
17037                     return;
17038                 }
17039                 
17040             };
17041         }else if(Roo.isSafari){
17042             return function(e){
17043                 var k = e.getKey();
17044                 
17045                 if(k == e.TAB){
17046                     e.stopEvent();
17047                     this.execCmd('InsertText','\t');
17048                     this.deferFocus();
17049                     return;
17050                 }
17051                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17052                     this.cleanUpPaste.defer(100, this);
17053                     return;
17054                 }
17055                 
17056              };
17057         }
17058     }(),
17059     
17060     getAllAncestors: function()
17061     {
17062         var p = this.getSelectedNode();
17063         var a = [];
17064         if (!p) {
17065             a.push(p); // push blank onto stack..
17066             p = this.getParentElement();
17067         }
17068         
17069         
17070         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17071             a.push(p);
17072             p = p.parentNode;
17073         }
17074         a.push(this.doc.body);
17075         return a;
17076     },
17077     lastSel : false,
17078     lastSelNode : false,
17079     
17080     
17081     getSelection : function() 
17082     {
17083         this.assignDocWin();
17084         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17085     },
17086     
17087     getSelectedNode: function() 
17088     {
17089         // this may only work on Gecko!!!
17090         
17091         // should we cache this!!!!
17092         
17093         
17094         
17095          
17096         var range = this.createRange(this.getSelection()).cloneRange();
17097         
17098         if (Roo.isIE) {
17099             var parent = range.parentElement();
17100             while (true) {
17101                 var testRange = range.duplicate();
17102                 testRange.moveToElementText(parent);
17103                 if (testRange.inRange(range)) {
17104                     break;
17105                 }
17106                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17107                     break;
17108                 }
17109                 parent = parent.parentElement;
17110             }
17111             return parent;
17112         }
17113         
17114         // is ancestor a text element.
17115         var ac =  range.commonAncestorContainer;
17116         if (ac.nodeType == 3) {
17117             ac = ac.parentNode;
17118         }
17119         
17120         var ar = ac.childNodes;
17121          
17122         var nodes = [];
17123         var other_nodes = [];
17124         var has_other_nodes = false;
17125         for (var i=0;i<ar.length;i++) {
17126             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17127                 continue;
17128             }
17129             // fullly contained node.
17130             
17131             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17132                 nodes.push(ar[i]);
17133                 continue;
17134             }
17135             
17136             // probably selected..
17137             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17138                 other_nodes.push(ar[i]);
17139                 continue;
17140             }
17141             // outer..
17142             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17143                 continue;
17144             }
17145             
17146             
17147             has_other_nodes = true;
17148         }
17149         if (!nodes.length && other_nodes.length) {
17150             nodes= other_nodes;
17151         }
17152         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17153             return false;
17154         }
17155         
17156         return nodes[0];
17157     },
17158     createRange: function(sel)
17159     {
17160         // this has strange effects when using with 
17161         // top toolbar - not sure if it's a great idea.
17162         //this.editor.contentWindow.focus();
17163         if (typeof sel != "undefined") {
17164             try {
17165                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17166             } catch(e) {
17167                 return this.doc.createRange();
17168             }
17169         } else {
17170             return this.doc.createRange();
17171         }
17172     },
17173     getParentElement: function()
17174     {
17175         
17176         this.assignDocWin();
17177         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17178         
17179         var range = this.createRange(sel);
17180          
17181         try {
17182             var p = range.commonAncestorContainer;
17183             while (p.nodeType == 3) { // text node
17184                 p = p.parentNode;
17185             }
17186             return p;
17187         } catch (e) {
17188             return null;
17189         }
17190     
17191     },
17192     /***
17193      *
17194      * Range intersection.. the hard stuff...
17195      *  '-1' = before
17196      *  '0' = hits..
17197      *  '1' = after.
17198      *         [ -- selected range --- ]
17199      *   [fail]                        [fail]
17200      *
17201      *    basically..
17202      *      if end is before start or  hits it. fail.
17203      *      if start is after end or hits it fail.
17204      *
17205      *   if either hits (but other is outside. - then it's not 
17206      *   
17207      *    
17208      **/
17209     
17210     
17211     // @see http://www.thismuchiknow.co.uk/?p=64.
17212     rangeIntersectsNode : function(range, node)
17213     {
17214         var nodeRange = node.ownerDocument.createRange();
17215         try {
17216             nodeRange.selectNode(node);
17217         } catch (e) {
17218             nodeRange.selectNodeContents(node);
17219         }
17220     
17221         var rangeStartRange = range.cloneRange();
17222         rangeStartRange.collapse(true);
17223     
17224         var rangeEndRange = range.cloneRange();
17225         rangeEndRange.collapse(false);
17226     
17227         var nodeStartRange = nodeRange.cloneRange();
17228         nodeStartRange.collapse(true);
17229     
17230         var nodeEndRange = nodeRange.cloneRange();
17231         nodeEndRange.collapse(false);
17232     
17233         return rangeStartRange.compareBoundaryPoints(
17234                  Range.START_TO_START, nodeEndRange) == -1 &&
17235                rangeEndRange.compareBoundaryPoints(
17236                  Range.START_TO_START, nodeStartRange) == 1;
17237         
17238          
17239     },
17240     rangeCompareNode : function(range, node)
17241     {
17242         var nodeRange = node.ownerDocument.createRange();
17243         try {
17244             nodeRange.selectNode(node);
17245         } catch (e) {
17246             nodeRange.selectNodeContents(node);
17247         }
17248         
17249         
17250         range.collapse(true);
17251     
17252         nodeRange.collapse(true);
17253      
17254         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17255         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17256          
17257         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17258         
17259         var nodeIsBefore   =  ss == 1;
17260         var nodeIsAfter    = ee == -1;
17261         
17262         if (nodeIsBefore && nodeIsAfter)
17263             return 0; // outer
17264         if (!nodeIsBefore && nodeIsAfter)
17265             return 1; //right trailed.
17266         
17267         if (nodeIsBefore && !nodeIsAfter)
17268             return 2;  // left trailed.
17269         // fully contined.
17270         return 3;
17271     },
17272
17273     // private? - in a new class?
17274     cleanUpPaste :  function()
17275     {
17276         // cleans up the whole document..
17277         Roo.log('cleanuppaste');
17278         
17279         this.cleanUpChildren(this.doc.body);
17280         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17281         if (clean != this.doc.body.innerHTML) {
17282             this.doc.body.innerHTML = clean;
17283         }
17284         
17285     },
17286     
17287     cleanWordChars : function(input) {// change the chars to hex code
17288         var he = Roo.HtmlEditorCore;
17289         
17290         var output = input;
17291         Roo.each(he.swapCodes, function(sw) { 
17292             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17293             
17294             output = output.replace(swapper, sw[1]);
17295         });
17296         
17297         return output;
17298     },
17299     
17300     
17301     cleanUpChildren : function (n)
17302     {
17303         if (!n.childNodes.length) {
17304             return;
17305         }
17306         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17307            this.cleanUpChild(n.childNodes[i]);
17308         }
17309     },
17310     
17311     
17312         
17313     
17314     cleanUpChild : function (node)
17315     {
17316         var ed = this;
17317         //console.log(node);
17318         if (node.nodeName == "#text") {
17319             // clean up silly Windows -- stuff?
17320             return; 
17321         }
17322         if (node.nodeName == "#comment") {
17323             node.parentNode.removeChild(node);
17324             // clean up silly Windows -- stuff?
17325             return; 
17326         }
17327         var lcname = node.tagName.toLowerCase();
17328         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17329         // whitelist of tags..
17330         
17331         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17332             // remove node.
17333             node.parentNode.removeChild(node);
17334             return;
17335             
17336         }
17337         
17338         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17339         
17340         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17341         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17342         
17343         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17344         //    remove_keep_children = true;
17345         //}
17346         
17347         if (remove_keep_children) {
17348             this.cleanUpChildren(node);
17349             // inserts everything just before this node...
17350             while (node.childNodes.length) {
17351                 var cn = node.childNodes[0];
17352                 node.removeChild(cn);
17353                 node.parentNode.insertBefore(cn, node);
17354             }
17355             node.parentNode.removeChild(node);
17356             return;
17357         }
17358         
17359         if (!node.attributes || !node.attributes.length) {
17360             this.cleanUpChildren(node);
17361             return;
17362         }
17363         
17364         function cleanAttr(n,v)
17365         {
17366             
17367             if (v.match(/^\./) || v.match(/^\//)) {
17368                 return;
17369             }
17370             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17371                 return;
17372             }
17373             if (v.match(/^#/)) {
17374                 return;
17375             }
17376 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17377             node.removeAttribute(n);
17378             
17379         }
17380         
17381         var cwhite = this.cwhite;
17382         var cblack = this.cblack;
17383             
17384         function cleanStyle(n,v)
17385         {
17386             if (v.match(/expression/)) { //XSS?? should we even bother..
17387                 node.removeAttribute(n);
17388                 return;
17389             }
17390             
17391             var parts = v.split(/;/);
17392             var clean = [];
17393             
17394             Roo.each(parts, function(p) {
17395                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17396                 if (!p.length) {
17397                     return true;
17398                 }
17399                 var l = p.split(':').shift().replace(/\s+/g,'');
17400                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17401                 
17402                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17403 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17404                     //node.removeAttribute(n);
17405                     return true;
17406                 }
17407                 //Roo.log()
17408                 // only allow 'c whitelisted system attributes'
17409                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17410 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17411                     //node.removeAttribute(n);
17412                     return true;
17413                 }
17414                 
17415                 
17416                  
17417                 
17418                 clean.push(p);
17419                 return true;
17420             });
17421             if (clean.length) { 
17422                 node.setAttribute(n, clean.join(';'));
17423             } else {
17424                 node.removeAttribute(n);
17425             }
17426             
17427         }
17428         
17429         
17430         for (var i = node.attributes.length-1; i > -1 ; i--) {
17431             var a = node.attributes[i];
17432             //console.log(a);
17433             
17434             if (a.name.toLowerCase().substr(0,2)=='on')  {
17435                 node.removeAttribute(a.name);
17436                 continue;
17437             }
17438             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17439                 node.removeAttribute(a.name);
17440                 continue;
17441             }
17442             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17443                 cleanAttr(a.name,a.value); // fixme..
17444                 continue;
17445             }
17446             if (a.name == 'style') {
17447                 cleanStyle(a.name,a.value);
17448                 continue;
17449             }
17450             /// clean up MS crap..
17451             // tecnically this should be a list of valid class'es..
17452             
17453             
17454             if (a.name == 'class') {
17455                 if (a.value.match(/^Mso/)) {
17456                     node.className = '';
17457                 }
17458                 
17459                 if (a.value.match(/body/)) {
17460                     node.className = '';
17461                 }
17462                 continue;
17463             }
17464             
17465             // style cleanup!?
17466             // class cleanup?
17467             
17468         }
17469         
17470         
17471         this.cleanUpChildren(node);
17472         
17473         
17474     },
17475     /**
17476      * Clean up MS wordisms...
17477      */
17478     cleanWord : function(node)
17479     {
17480         var _t = this;
17481         var cleanWordChildren = function()
17482         {
17483             if (!node.childNodes.length) {
17484                 return;
17485             }
17486             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17487                _t.cleanWord(node.childNodes[i]);
17488             }
17489         }
17490         
17491         
17492         if (!node) {
17493             this.cleanWord(this.doc.body);
17494             return;
17495         }
17496         if (node.nodeName == "#text") {
17497             // clean up silly Windows -- stuff?
17498             return; 
17499         }
17500         if (node.nodeName == "#comment") {
17501             node.parentNode.removeChild(node);
17502             // clean up silly Windows -- stuff?
17503             return; 
17504         }
17505         
17506         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17507             node.parentNode.removeChild(node);
17508             return;
17509         }
17510         
17511         // remove - but keep children..
17512         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17513             while (node.childNodes.length) {
17514                 var cn = node.childNodes[0];
17515                 node.removeChild(cn);
17516                 node.parentNode.insertBefore(cn, node);
17517             }
17518             node.parentNode.removeChild(node);
17519             cleanWordChildren();
17520             return;
17521         }
17522         // clean styles
17523         if (node.className.length) {
17524             
17525             var cn = node.className.split(/\W+/);
17526             var cna = [];
17527             Roo.each(cn, function(cls) {
17528                 if (cls.match(/Mso[a-zA-Z]+/)) {
17529                     return;
17530                 }
17531                 cna.push(cls);
17532             });
17533             node.className = cna.length ? cna.join(' ') : '';
17534             if (!cna.length) {
17535                 node.removeAttribute("class");
17536             }
17537         }
17538         
17539         if (node.hasAttribute("lang")) {
17540             node.removeAttribute("lang");
17541         }
17542         
17543         if (node.hasAttribute("style")) {
17544             
17545             var styles = node.getAttribute("style").split(";");
17546             var nstyle = [];
17547             Roo.each(styles, function(s) {
17548                 if (!s.match(/:/)) {
17549                     return;
17550                 }
17551                 var kv = s.split(":");
17552                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17553                     return;
17554                 }
17555                 // what ever is left... we allow.
17556                 nstyle.push(s);
17557             });
17558             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17559             if (!nstyle.length) {
17560                 node.removeAttribute('style');
17561             }
17562         }
17563         
17564         cleanWordChildren();
17565         
17566         
17567     },
17568     domToHTML : function(currentElement, depth, nopadtext) {
17569         
17570         depth = depth || 0;
17571         nopadtext = nopadtext || false;
17572     
17573         if (!currentElement) {
17574             return this.domToHTML(this.doc.body);
17575         }
17576         
17577         //Roo.log(currentElement);
17578         var j;
17579         var allText = false;
17580         var nodeName = currentElement.nodeName;
17581         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17582         
17583         if  (nodeName == '#text') {
17584             return currentElement.nodeValue;
17585         }
17586         
17587         
17588         var ret = '';
17589         if (nodeName != 'BODY') {
17590              
17591             var i = 0;
17592             // Prints the node tagName, such as <A>, <IMG>, etc
17593             if (tagName) {
17594                 var attr = [];
17595                 for(i = 0; i < currentElement.attributes.length;i++) {
17596                     // quoting?
17597                     var aname = currentElement.attributes.item(i).name;
17598                     if (!currentElement.attributes.item(i).value.length) {
17599                         continue;
17600                     }
17601                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17602                 }
17603                 
17604                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17605             } 
17606             else {
17607                 
17608                 // eack
17609             }
17610         } else {
17611             tagName = false;
17612         }
17613         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17614             return ret;
17615         }
17616         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17617             nopadtext = true;
17618         }
17619         
17620         
17621         // Traverse the tree
17622         i = 0;
17623         var currentElementChild = currentElement.childNodes.item(i);
17624         var allText = true;
17625         var innerHTML  = '';
17626         lastnode = '';
17627         while (currentElementChild) {
17628             // Formatting code (indent the tree so it looks nice on the screen)
17629             var nopad = nopadtext;
17630             if (lastnode == 'SPAN') {
17631                 nopad  = true;
17632             }
17633             // text
17634             if  (currentElementChild.nodeName == '#text') {
17635                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17636                 if (!nopad && toadd.length > 80) {
17637                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17638                 }
17639                 innerHTML  += toadd;
17640                 
17641                 i++;
17642                 currentElementChild = currentElement.childNodes.item(i);
17643                 lastNode = '';
17644                 continue;
17645             }
17646             allText = false;
17647             
17648             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17649                 
17650             // Recursively traverse the tree structure of the child node
17651             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17652             lastnode = currentElementChild.nodeName;
17653             i++;
17654             currentElementChild=currentElement.childNodes.item(i);
17655         }
17656         
17657         ret += innerHTML;
17658         
17659         if (!allText) {
17660                 // The remaining code is mostly for formatting the tree
17661             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17662         }
17663         
17664         
17665         if (tagName) {
17666             ret+= "</"+tagName+">";
17667         }
17668         return ret;
17669         
17670     },
17671         
17672     applyBlacklists : function()
17673     {
17674         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17675         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17676         
17677         this.white = [];
17678         this.black = [];
17679         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17680             if (b.indexOf(tag) > -1) {
17681                 return;
17682             }
17683             this.white.push(tag);
17684             
17685         }, this);
17686         
17687         Roo.each(w, function(tag) {
17688             if (b.indexOf(tag) > -1) {
17689                 return;
17690             }
17691             if (this.white.indexOf(tag) > -1) {
17692                 return;
17693             }
17694             this.white.push(tag);
17695             
17696         }, this);
17697         
17698         
17699         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17700             if (w.indexOf(tag) > -1) {
17701                 return;
17702             }
17703             this.black.push(tag);
17704             
17705         }, this);
17706         
17707         Roo.each(b, function(tag) {
17708             if (w.indexOf(tag) > -1) {
17709                 return;
17710             }
17711             if (this.black.indexOf(tag) > -1) {
17712                 return;
17713             }
17714             this.black.push(tag);
17715             
17716         }, this);
17717         
17718         
17719         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17720         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17721         
17722         this.cwhite = [];
17723         this.cblack = [];
17724         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17725             if (b.indexOf(tag) > -1) {
17726                 return;
17727             }
17728             this.cwhite.push(tag);
17729             
17730         }, this);
17731         
17732         Roo.each(w, function(tag) {
17733             if (b.indexOf(tag) > -1) {
17734                 return;
17735             }
17736             if (this.cwhite.indexOf(tag) > -1) {
17737                 return;
17738             }
17739             this.cwhite.push(tag);
17740             
17741         }, this);
17742         
17743         
17744         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17745             if (w.indexOf(tag) > -1) {
17746                 return;
17747             }
17748             this.cblack.push(tag);
17749             
17750         }, this);
17751         
17752         Roo.each(b, function(tag) {
17753             if (w.indexOf(tag) > -1) {
17754                 return;
17755             }
17756             if (this.cblack.indexOf(tag) > -1) {
17757                 return;
17758             }
17759             this.cblack.push(tag);
17760             
17761         }, this);
17762     }
17763     
17764     // hide stuff that is not compatible
17765     /**
17766      * @event blur
17767      * @hide
17768      */
17769     /**
17770      * @event change
17771      * @hide
17772      */
17773     /**
17774      * @event focus
17775      * @hide
17776      */
17777     /**
17778      * @event specialkey
17779      * @hide
17780      */
17781     /**
17782      * @cfg {String} fieldClass @hide
17783      */
17784     /**
17785      * @cfg {String} focusClass @hide
17786      */
17787     /**
17788      * @cfg {String} autoCreate @hide
17789      */
17790     /**
17791      * @cfg {String} inputType @hide
17792      */
17793     /**
17794      * @cfg {String} invalidClass @hide
17795      */
17796     /**
17797      * @cfg {String} invalidText @hide
17798      */
17799     /**
17800      * @cfg {String} msgFx @hide
17801      */
17802     /**
17803      * @cfg {String} validateOnBlur @hide
17804      */
17805 });
17806
17807 Roo.HtmlEditorCore.white = [
17808         'area', 'br', 'img', 'input', 'hr', 'wbr',
17809         
17810        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17811        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17812        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17813        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17814        'table',   'ul',         'xmp', 
17815        
17816        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17817       'thead',   'tr', 
17818      
17819       'dir', 'menu', 'ol', 'ul', 'dl',
17820        
17821       'embed',  'object'
17822 ];
17823
17824
17825 Roo.HtmlEditorCore.black = [
17826     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17827         'applet', // 
17828         'base',   'basefont', 'bgsound', 'blink',  'body', 
17829         'frame',  'frameset', 'head',    'html',   'ilayer', 
17830         'iframe', 'layer',  'link',     'meta',    'object',   
17831         'script', 'style' ,'title',  'xml' // clean later..
17832 ];
17833 Roo.HtmlEditorCore.clean = [
17834     'script', 'style', 'title', 'xml'
17835 ];
17836 Roo.HtmlEditorCore.remove = [
17837     'font'
17838 ];
17839 // attributes..
17840
17841 Roo.HtmlEditorCore.ablack = [
17842     'on'
17843 ];
17844     
17845 Roo.HtmlEditorCore.aclean = [ 
17846     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17847 ];
17848
17849 // protocols..
17850 Roo.HtmlEditorCore.pwhite= [
17851         'http',  'https',  'mailto'
17852 ];
17853
17854 // white listed style attributes.
17855 Roo.HtmlEditorCore.cwhite= [
17856       //  'text-align', /// default is to allow most things..
17857       
17858          
17859 //        'font-size'//??
17860 ];
17861
17862 // black listed style attributes.
17863 Roo.HtmlEditorCore.cblack= [
17864       //  'font-size' -- this can be set by the project 
17865 ];
17866
17867
17868 Roo.HtmlEditorCore.swapCodes   =[ 
17869     [    8211, "--" ], 
17870     [    8212, "--" ], 
17871     [    8216,  "'" ],  
17872     [    8217, "'" ],  
17873     [    8220, '"' ],  
17874     [    8221, '"' ],  
17875     [    8226, "*" ],  
17876     [    8230, "..." ]
17877 ]; 
17878
17879     /*
17880  * - LGPL
17881  *
17882  * HtmlEditor
17883  * 
17884  */
17885
17886 /**
17887  * @class Roo.bootstrap.HtmlEditor
17888  * @extends Roo.bootstrap.TextArea
17889  * Bootstrap HtmlEditor class
17890
17891  * @constructor
17892  * Create a new HtmlEditor
17893  * @param {Object} config The config object
17894  */
17895
17896 Roo.bootstrap.HtmlEditor = function(config){
17897     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17898     if (!this.toolbars) {
17899         this.toolbars = [];
17900     }
17901     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17902     this.addEvents({
17903             /**
17904              * @event initialize
17905              * Fires when the editor is fully initialized (including the iframe)
17906              * @param {HtmlEditor} this
17907              */
17908             initialize: true,
17909             /**
17910              * @event activate
17911              * Fires when the editor is first receives the focus. Any insertion must wait
17912              * until after this event.
17913              * @param {HtmlEditor} this
17914              */
17915             activate: true,
17916              /**
17917              * @event beforesync
17918              * Fires before the textarea is updated with content from the editor iframe. Return false
17919              * to cancel the sync.
17920              * @param {HtmlEditor} this
17921              * @param {String} html
17922              */
17923             beforesync: true,
17924              /**
17925              * @event beforepush
17926              * Fires before the iframe editor is updated with content from the textarea. Return false
17927              * to cancel the push.
17928              * @param {HtmlEditor} this
17929              * @param {String} html
17930              */
17931             beforepush: true,
17932              /**
17933              * @event sync
17934              * Fires when the textarea is updated with content from the editor iframe.
17935              * @param {HtmlEditor} this
17936              * @param {String} html
17937              */
17938             sync: true,
17939              /**
17940              * @event push
17941              * Fires when the iframe editor is updated with content from the textarea.
17942              * @param {HtmlEditor} this
17943              * @param {String} html
17944              */
17945             push: true,
17946              /**
17947              * @event editmodechange
17948              * Fires when the editor switches edit modes
17949              * @param {HtmlEditor} this
17950              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17951              */
17952             editmodechange: true,
17953             /**
17954              * @event editorevent
17955              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17956              * @param {HtmlEditor} this
17957              */
17958             editorevent: true,
17959             /**
17960              * @event firstfocus
17961              * Fires when on first focus - needed by toolbars..
17962              * @param {HtmlEditor} this
17963              */
17964             firstfocus: true,
17965             /**
17966              * @event autosave
17967              * Auto save the htmlEditor value as a file into Events
17968              * @param {HtmlEditor} this
17969              */
17970             autosave: true,
17971             /**
17972              * @event savedpreview
17973              * preview the saved version of htmlEditor
17974              * @param {HtmlEditor} this
17975              */
17976             savedpreview: true
17977         });
17978 };
17979
17980
17981 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17982     
17983     
17984       /**
17985      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17986      */
17987     toolbars : false,
17988    
17989      /**
17990      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17991      *                        Roo.resizable.
17992      */
17993     resizable : false,
17994      /**
17995      * @cfg {Number} height (in pixels)
17996      */   
17997     height: 300,
17998    /**
17999      * @cfg {Number} width (in pixels)
18000      */   
18001     width: false,
18002     
18003     /**
18004      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18005      * 
18006      */
18007     stylesheets: false,
18008     
18009     // id of frame..
18010     frameId: false,
18011     
18012     // private properties
18013     validationEvent : false,
18014     deferHeight: true,
18015     initialized : false,
18016     activated : false,
18017     
18018     onFocus : Roo.emptyFn,
18019     iframePad:3,
18020     hideMode:'offsets',
18021     
18022     
18023     tbContainer : false,
18024     
18025     toolbarContainer :function() {
18026         return this.wrap.select('.x-html-editor-tb',true).first();
18027     },
18028
18029     /**
18030      * Protected method that will not generally be called directly. It
18031      * is called when the editor creates its toolbar. Override this method if you need to
18032      * add custom toolbar buttons.
18033      * @param {HtmlEditor} editor
18034      */
18035     createToolbar : function(){
18036         
18037         Roo.log("create toolbars");
18038         
18039         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18040         this.toolbars[0].render(this.toolbarContainer());
18041         
18042         return;
18043         
18044 //        if (!editor.toolbars || !editor.toolbars.length) {
18045 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18046 //        }
18047 //        
18048 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18049 //            editor.toolbars[i] = Roo.factory(
18050 //                    typeof(editor.toolbars[i]) == 'string' ?
18051 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18052 //                Roo.bootstrap.HtmlEditor);
18053 //            editor.toolbars[i].init(editor);
18054 //        }
18055     },
18056
18057      
18058     // private
18059     onRender : function(ct, position)
18060     {
18061        // Roo.log("Call onRender: " + this.xtype);
18062         var _t = this;
18063         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18064       
18065         this.wrap = this.inputEl().wrap({
18066             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18067         });
18068         
18069         this.editorcore.onRender(ct, position);
18070          
18071         if (this.resizable) {
18072             this.resizeEl = new Roo.Resizable(this.wrap, {
18073                 pinned : true,
18074                 wrap: true,
18075                 dynamic : true,
18076                 minHeight : this.height,
18077                 height: this.height,
18078                 handles : this.resizable,
18079                 width: this.width,
18080                 listeners : {
18081                     resize : function(r, w, h) {
18082                         _t.onResize(w,h); // -something
18083                     }
18084                 }
18085             });
18086             
18087         }
18088         this.createToolbar(this);
18089        
18090         
18091         if(!this.width && this.resizable){
18092             this.setSize(this.wrap.getSize());
18093         }
18094         if (this.resizeEl) {
18095             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18096             // should trigger onReize..
18097         }
18098         
18099     },
18100
18101     // private
18102     onResize : function(w, h)
18103     {
18104         Roo.log('resize: ' +w + ',' + h );
18105         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18106         var ew = false;
18107         var eh = false;
18108         
18109         if(this.inputEl() ){
18110             if(typeof w == 'number'){
18111                 var aw = w - this.wrap.getFrameWidth('lr');
18112                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18113                 ew = aw;
18114             }
18115             if(typeof h == 'number'){
18116                  var tbh = -11;  // fixme it needs to tool bar size!
18117                 for (var i =0; i < this.toolbars.length;i++) {
18118                     // fixme - ask toolbars for heights?
18119                     tbh += this.toolbars[i].el.getHeight();
18120                     //if (this.toolbars[i].footer) {
18121                     //    tbh += this.toolbars[i].footer.el.getHeight();
18122                     //}
18123                 }
18124               
18125                 
18126                 
18127                 
18128                 
18129                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18130                 ah -= 5; // knock a few pixes off for look..
18131                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18132                 var eh = ah;
18133             }
18134         }
18135         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18136         this.editorcore.onResize(ew,eh);
18137         
18138     },
18139
18140     /**
18141      * Toggles the editor between standard and source edit mode.
18142      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18143      */
18144     toggleSourceEdit : function(sourceEditMode)
18145     {
18146         this.editorcore.toggleSourceEdit(sourceEditMode);
18147         
18148         if(this.editorcore.sourceEditMode){
18149             Roo.log('editor - showing textarea');
18150             
18151 //            Roo.log('in');
18152 //            Roo.log(this.syncValue());
18153             this.syncValue();
18154             this.inputEl().removeClass(['hide', 'x-hidden']);
18155             this.inputEl().dom.removeAttribute('tabIndex');
18156             this.inputEl().focus();
18157         }else{
18158             Roo.log('editor - hiding textarea');
18159 //            Roo.log('out')
18160 //            Roo.log(this.pushValue()); 
18161             this.pushValue();
18162             
18163             this.inputEl().addClass(['hide', 'x-hidden']);
18164             this.inputEl().dom.setAttribute('tabIndex', -1);
18165             //this.deferFocus();
18166         }
18167          
18168         if(this.resizable){
18169             this.setSize(this.wrap.getSize());
18170         }
18171         
18172         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18173     },
18174  
18175     // private (for BoxComponent)
18176     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18177
18178     // private (for BoxComponent)
18179     getResizeEl : function(){
18180         return this.wrap;
18181     },
18182
18183     // private (for BoxComponent)
18184     getPositionEl : function(){
18185         return this.wrap;
18186     },
18187
18188     // private
18189     initEvents : function(){
18190         this.originalValue = this.getValue();
18191     },
18192
18193 //    /**
18194 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18195 //     * @method
18196 //     */
18197 //    markInvalid : Roo.emptyFn,
18198 //    /**
18199 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18200 //     * @method
18201 //     */
18202 //    clearInvalid : Roo.emptyFn,
18203
18204     setValue : function(v){
18205         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18206         this.editorcore.pushValue();
18207     },
18208
18209      
18210     // private
18211     deferFocus : function(){
18212         this.focus.defer(10, this);
18213     },
18214
18215     // doc'ed in Field
18216     focus : function(){
18217         this.editorcore.focus();
18218         
18219     },
18220       
18221
18222     // private
18223     onDestroy : function(){
18224         
18225         
18226         
18227         if(this.rendered){
18228             
18229             for (var i =0; i < this.toolbars.length;i++) {
18230                 // fixme - ask toolbars for heights?
18231                 this.toolbars[i].onDestroy();
18232             }
18233             
18234             this.wrap.dom.innerHTML = '';
18235             this.wrap.remove();
18236         }
18237     },
18238
18239     // private
18240     onFirstFocus : function(){
18241         //Roo.log("onFirstFocus");
18242         this.editorcore.onFirstFocus();
18243          for (var i =0; i < this.toolbars.length;i++) {
18244             this.toolbars[i].onFirstFocus();
18245         }
18246         
18247     },
18248     
18249     // private
18250     syncValue : function()
18251     {   
18252         this.editorcore.syncValue();
18253     },
18254     
18255     pushValue : function()
18256     {   
18257         this.editorcore.pushValue();
18258     }
18259      
18260     
18261     // hide stuff that is not compatible
18262     /**
18263      * @event blur
18264      * @hide
18265      */
18266     /**
18267      * @event change
18268      * @hide
18269      */
18270     /**
18271      * @event focus
18272      * @hide
18273      */
18274     /**
18275      * @event specialkey
18276      * @hide
18277      */
18278     /**
18279      * @cfg {String} fieldClass @hide
18280      */
18281     /**
18282      * @cfg {String} focusClass @hide
18283      */
18284     /**
18285      * @cfg {String} autoCreate @hide
18286      */
18287     /**
18288      * @cfg {String} inputType @hide
18289      */
18290     /**
18291      * @cfg {String} invalidClass @hide
18292      */
18293     /**
18294      * @cfg {String} invalidText @hide
18295      */
18296     /**
18297      * @cfg {String} msgFx @hide
18298      */
18299     /**
18300      * @cfg {String} validateOnBlur @hide
18301      */
18302 });
18303  
18304     
18305    
18306    
18307    
18308       
18309 Roo.namespace('Roo.bootstrap.htmleditor');
18310 /**
18311  * @class Roo.bootstrap.HtmlEditorToolbar1
18312  * Basic Toolbar
18313  * 
18314  * Usage:
18315  *
18316  new Roo.bootstrap.HtmlEditor({
18317     ....
18318     toolbars : [
18319         new Roo.bootstrap.HtmlEditorToolbar1({
18320             disable : { fonts: 1 , format: 1, ..., ... , ...],
18321             btns : [ .... ]
18322         })
18323     }
18324      
18325  * 
18326  * @cfg {Object} disable List of elements to disable..
18327  * @cfg {Array} btns List of additional buttons.
18328  * 
18329  * 
18330  * NEEDS Extra CSS? 
18331  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18332  */
18333  
18334 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18335 {
18336     
18337     Roo.apply(this, config);
18338     
18339     // default disabled, based on 'good practice'..
18340     this.disable = this.disable || {};
18341     Roo.applyIf(this.disable, {
18342         fontSize : true,
18343         colors : true,
18344         specialElements : true
18345     });
18346     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18347     
18348     this.editor = config.editor;
18349     this.editorcore = config.editor.editorcore;
18350     
18351     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18352     
18353     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18354     // dont call parent... till later.
18355 }
18356 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18357      
18358     bar : true,
18359     
18360     editor : false,
18361     editorcore : false,
18362     
18363     
18364     formats : [
18365         "p" ,  
18366         "h1","h2","h3","h4","h5","h6", 
18367         "pre", "code", 
18368         "abbr", "acronym", "address", "cite", "samp", "var",
18369         'div','span'
18370     ],
18371     
18372     onRender : function(ct, position)
18373     {
18374        // Roo.log("Call onRender: " + this.xtype);
18375         
18376        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18377        Roo.log(this.el);
18378        this.el.dom.style.marginBottom = '0';
18379        var _this = this;
18380        var editorcore = this.editorcore;
18381        var editor= this.editor;
18382        
18383        var children = [];
18384        var btn = function(id,cmd , toggle, handler){
18385        
18386             var  event = toggle ? 'toggle' : 'click';
18387        
18388             var a = {
18389                 size : 'sm',
18390                 xtype: 'Button',
18391                 xns: Roo.bootstrap,
18392                 glyphicon : id,
18393                 cmd : id || cmd,
18394                 enableToggle:toggle !== false,
18395                 //html : 'submit'
18396                 pressed : toggle ? false : null,
18397                 listeners : {}
18398             }
18399             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18400                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18401             }
18402             children.push(a);
18403             return a;
18404        }
18405         
18406         var style = {
18407                 xtype: 'Button',
18408                 size : 'sm',
18409                 xns: Roo.bootstrap,
18410                 glyphicon : 'font',
18411                 //html : 'submit'
18412                 menu : {
18413                     xtype: 'Menu',
18414                     xns: Roo.bootstrap,
18415                     items:  []
18416                 }
18417         };
18418         Roo.each(this.formats, function(f) {
18419             style.menu.items.push({
18420                 xtype :'MenuItem',
18421                 xns: Roo.bootstrap,
18422                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18423                 tagname : f,
18424                 listeners : {
18425                     click : function()
18426                     {
18427                         editorcore.insertTag(this.tagname);
18428                         editor.focus();
18429                     }
18430                 }
18431                 
18432             });
18433         });
18434          children.push(style);   
18435             
18436             
18437         btn('bold',false,true);
18438         btn('italic',false,true);
18439         btn('align-left', 'justifyleft',true);
18440         btn('align-center', 'justifycenter',true);
18441         btn('align-right' , 'justifyright',true);
18442         btn('link', false, false, function(btn) {
18443             //Roo.log("create link?");
18444             var url = prompt(this.createLinkText, this.defaultLinkValue);
18445             if(url && url != 'http:/'+'/'){
18446                 this.editorcore.relayCmd('createlink', url);
18447             }
18448         }),
18449         btn('list','insertunorderedlist',true);
18450         btn('pencil', false,true, function(btn){
18451                 Roo.log(this);
18452                 
18453                 this.toggleSourceEdit(btn.pressed);
18454         });
18455         /*
18456         var cog = {
18457                 xtype: 'Button',
18458                 size : 'sm',
18459                 xns: Roo.bootstrap,
18460                 glyphicon : 'cog',
18461                 //html : 'submit'
18462                 menu : {
18463                     xtype: 'Menu',
18464                     xns: Roo.bootstrap,
18465                     items:  []
18466                 }
18467         };
18468         
18469         cog.menu.items.push({
18470             xtype :'MenuItem',
18471             xns: Roo.bootstrap,
18472             html : Clean styles,
18473             tagname : f,
18474             listeners : {
18475                 click : function()
18476                 {
18477                     editorcore.insertTag(this.tagname);
18478                     editor.focus();
18479                 }
18480             }
18481             
18482         });
18483        */
18484         
18485          
18486        this.xtype = 'NavSimplebar';
18487         
18488         for(var i=0;i< children.length;i++) {
18489             
18490             this.buttons.add(this.addxtypeChild(children[i]));
18491             
18492         }
18493         
18494         editor.on('editorevent', this.updateToolbar, this);
18495     },
18496     onBtnClick : function(id)
18497     {
18498        this.editorcore.relayCmd(id);
18499        this.editorcore.focus();
18500     },
18501     
18502     /**
18503      * Protected method that will not generally be called directly. It triggers
18504      * a toolbar update by reading the markup state of the current selection in the editor.
18505      */
18506     updateToolbar: function(){
18507
18508         if(!this.editorcore.activated){
18509             this.editor.onFirstFocus(); // is this neeed?
18510             return;
18511         }
18512
18513         var btns = this.buttons; 
18514         var doc = this.editorcore.doc;
18515         btns.get('bold').setActive(doc.queryCommandState('bold'));
18516         btns.get('italic').setActive(doc.queryCommandState('italic'));
18517         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18518         
18519         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18520         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18521         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18522         
18523         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18524         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18525          /*
18526         
18527         var ans = this.editorcore.getAllAncestors();
18528         if (this.formatCombo) {
18529             
18530             
18531             var store = this.formatCombo.store;
18532             this.formatCombo.setValue("");
18533             for (var i =0; i < ans.length;i++) {
18534                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18535                     // select it..
18536                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18537                     break;
18538                 }
18539             }
18540         }
18541         
18542         
18543         
18544         // hides menus... - so this cant be on a menu...
18545         Roo.bootstrap.MenuMgr.hideAll();
18546         */
18547         Roo.bootstrap.MenuMgr.hideAll();
18548         //this.editorsyncValue();
18549     },
18550     onFirstFocus: function() {
18551         this.buttons.each(function(item){
18552            item.enable();
18553         });
18554     },
18555     toggleSourceEdit : function(sourceEditMode){
18556         
18557           
18558         if(sourceEditMode){
18559             Roo.log("disabling buttons");
18560            this.buttons.each( function(item){
18561                 if(item.cmd != 'pencil'){
18562                     item.disable();
18563                 }
18564             });
18565           
18566         }else{
18567             Roo.log("enabling buttons");
18568             if(this.editorcore.initialized){
18569                 this.buttons.each( function(item){
18570                     item.enable();
18571                 });
18572             }
18573             
18574         }
18575         Roo.log("calling toggole on editor");
18576         // tell the editor that it's been pressed..
18577         this.editor.toggleSourceEdit(sourceEditMode);
18578        
18579     }
18580 });
18581
18582
18583
18584
18585
18586 /**
18587  * @class Roo.bootstrap.Table.AbstractSelectionModel
18588  * @extends Roo.util.Observable
18589  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18590  * implemented by descendant classes.  This class should not be directly instantiated.
18591  * @constructor
18592  */
18593 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18594     this.locked = false;
18595     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18596 };
18597
18598
18599 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18600     /** @ignore Called by the grid automatically. Do not call directly. */
18601     init : function(grid){
18602         this.grid = grid;
18603         this.initEvents();
18604     },
18605
18606     /**
18607      * Locks the selections.
18608      */
18609     lock : function(){
18610         this.locked = true;
18611     },
18612
18613     /**
18614      * Unlocks the selections.
18615      */
18616     unlock : function(){
18617         this.locked = false;
18618     },
18619
18620     /**
18621      * Returns true if the selections are locked.
18622      * @return {Boolean}
18623      */
18624     isLocked : function(){
18625         return this.locked;
18626     }
18627 });
18628 /**
18629  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18630  * @class Roo.bootstrap.Table.RowSelectionModel
18631  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18632  * It supports multiple selections and keyboard selection/navigation. 
18633  * @constructor
18634  * @param {Object} config
18635  */
18636
18637 Roo.bootstrap.Table.RowSelectionModel = function(config){
18638     Roo.apply(this, config);
18639     this.selections = new Roo.util.MixedCollection(false, function(o){
18640         return o.id;
18641     });
18642
18643     this.last = false;
18644     this.lastActive = false;
18645
18646     this.addEvents({
18647         /**
18648              * @event selectionchange
18649              * Fires when the selection changes
18650              * @param {SelectionModel} this
18651              */
18652             "selectionchange" : true,
18653         /**
18654              * @event afterselectionchange
18655              * Fires after the selection changes (eg. by key press or clicking)
18656              * @param {SelectionModel} this
18657              */
18658             "afterselectionchange" : true,
18659         /**
18660              * @event beforerowselect
18661              * Fires when a row is selected being selected, return false to cancel.
18662              * @param {SelectionModel} this
18663              * @param {Number} rowIndex The selected index
18664              * @param {Boolean} keepExisting False if other selections will be cleared
18665              */
18666             "beforerowselect" : true,
18667         /**
18668              * @event rowselect
18669              * Fires when a row is selected.
18670              * @param {SelectionModel} this
18671              * @param {Number} rowIndex The selected index
18672              * @param {Roo.data.Record} r The record
18673              */
18674             "rowselect" : true,
18675         /**
18676              * @event rowdeselect
18677              * Fires when a row is deselected.
18678              * @param {SelectionModel} this
18679              * @param {Number} rowIndex The selected index
18680              */
18681         "rowdeselect" : true
18682     });
18683     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18684     this.locked = false;
18685 };
18686
18687 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18688     /**
18689      * @cfg {Boolean} singleSelect
18690      * True to allow selection of only one row at a time (defaults to false)
18691      */
18692     singleSelect : false,
18693
18694     // private
18695     initEvents : function(){
18696
18697         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18698             this.grid.on("mousedown", this.handleMouseDown, this);
18699         }else{ // allow click to work like normal
18700             this.grid.on("rowclick", this.handleDragableRowClick, this);
18701         }
18702
18703         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18704             "up" : function(e){
18705                 if(!e.shiftKey){
18706                     this.selectPrevious(e.shiftKey);
18707                 }else if(this.last !== false && this.lastActive !== false){
18708                     var last = this.last;
18709                     this.selectRange(this.last,  this.lastActive-1);
18710                     this.grid.getView().focusRow(this.lastActive);
18711                     if(last !== false){
18712                         this.last = last;
18713                     }
18714                 }else{
18715                     this.selectFirstRow();
18716                 }
18717                 this.fireEvent("afterselectionchange", this);
18718             },
18719             "down" : function(e){
18720                 if(!e.shiftKey){
18721                     this.selectNext(e.shiftKey);
18722                 }else if(this.last !== false && this.lastActive !== false){
18723                     var last = this.last;
18724                     this.selectRange(this.last,  this.lastActive+1);
18725                     this.grid.getView().focusRow(this.lastActive);
18726                     if(last !== false){
18727                         this.last = last;
18728                     }
18729                 }else{
18730                     this.selectFirstRow();
18731                 }
18732                 this.fireEvent("afterselectionchange", this);
18733             },
18734             scope: this
18735         });
18736
18737         var view = this.grid.view;
18738         view.on("refresh", this.onRefresh, this);
18739         view.on("rowupdated", this.onRowUpdated, this);
18740         view.on("rowremoved", this.onRemove, this);
18741     },
18742
18743     // private
18744     onRefresh : function(){
18745         var ds = this.grid.dataSource, i, v = this.grid.view;
18746         var s = this.selections;
18747         s.each(function(r){
18748             if((i = ds.indexOfId(r.id)) != -1){
18749                 v.onRowSelect(i);
18750             }else{
18751                 s.remove(r);
18752             }
18753         });
18754     },
18755
18756     // private
18757     onRemove : function(v, index, r){
18758         this.selections.remove(r);
18759     },
18760
18761     // private
18762     onRowUpdated : function(v, index, r){
18763         if(this.isSelected(r)){
18764             v.onRowSelect(index);
18765         }
18766     },
18767
18768     /**
18769      * Select records.
18770      * @param {Array} records The records to select
18771      * @param {Boolean} keepExisting (optional) True to keep existing selections
18772      */
18773     selectRecords : function(records, keepExisting){
18774         if(!keepExisting){
18775             this.clearSelections();
18776         }
18777         var ds = this.grid.dataSource;
18778         for(var i = 0, len = records.length; i < len; i++){
18779             this.selectRow(ds.indexOf(records[i]), true);
18780         }
18781     },
18782
18783     /**
18784      * Gets the number of selected rows.
18785      * @return {Number}
18786      */
18787     getCount : function(){
18788         return this.selections.length;
18789     },
18790
18791     /**
18792      * Selects the first row in the grid.
18793      */
18794     selectFirstRow : function(){
18795         this.selectRow(0);
18796     },
18797
18798     /**
18799      * Select the last row.
18800      * @param {Boolean} keepExisting (optional) True to keep existing selections
18801      */
18802     selectLastRow : function(keepExisting){
18803         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18804     },
18805
18806     /**
18807      * Selects the row immediately following the last selected row.
18808      * @param {Boolean} keepExisting (optional) True to keep existing selections
18809      */
18810     selectNext : function(keepExisting){
18811         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18812             this.selectRow(this.last+1, keepExisting);
18813             this.grid.getView().focusRow(this.last);
18814         }
18815     },
18816
18817     /**
18818      * Selects the row that precedes the last selected row.
18819      * @param {Boolean} keepExisting (optional) True to keep existing selections
18820      */
18821     selectPrevious : function(keepExisting){
18822         if(this.last){
18823             this.selectRow(this.last-1, keepExisting);
18824             this.grid.getView().focusRow(this.last);
18825         }
18826     },
18827
18828     /**
18829      * Returns the selected records
18830      * @return {Array} Array of selected records
18831      */
18832     getSelections : function(){
18833         return [].concat(this.selections.items);
18834     },
18835
18836     /**
18837      * Returns the first selected record.
18838      * @return {Record}
18839      */
18840     getSelected : function(){
18841         return this.selections.itemAt(0);
18842     },
18843
18844
18845     /**
18846      * Clears all selections.
18847      */
18848     clearSelections : function(fast){
18849         if(this.locked) return;
18850         if(fast !== true){
18851             var ds = this.grid.dataSource;
18852             var s = this.selections;
18853             s.each(function(r){
18854                 this.deselectRow(ds.indexOfId(r.id));
18855             }, this);
18856             s.clear();
18857         }else{
18858             this.selections.clear();
18859         }
18860         this.last = false;
18861     },
18862
18863
18864     /**
18865      * Selects all rows.
18866      */
18867     selectAll : function(){
18868         if(this.locked) return;
18869         this.selections.clear();
18870         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18871             this.selectRow(i, true);
18872         }
18873     },
18874
18875     /**
18876      * Returns True if there is a selection.
18877      * @return {Boolean}
18878      */
18879     hasSelection : function(){
18880         return this.selections.length > 0;
18881     },
18882
18883     /**
18884      * Returns True if the specified row is selected.
18885      * @param {Number/Record} record The record or index of the record to check
18886      * @return {Boolean}
18887      */
18888     isSelected : function(index){
18889         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18890         return (r && this.selections.key(r.id) ? true : false);
18891     },
18892
18893     /**
18894      * Returns True if the specified record id is selected.
18895      * @param {String} id The id of record to check
18896      * @return {Boolean}
18897      */
18898     isIdSelected : function(id){
18899         return (this.selections.key(id) ? true : false);
18900     },
18901
18902     // private
18903     handleMouseDown : function(e, t){
18904         var view = this.grid.getView(), rowIndex;
18905         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18906             return;
18907         };
18908         if(e.shiftKey && this.last !== false){
18909             var last = this.last;
18910             this.selectRange(last, rowIndex, e.ctrlKey);
18911             this.last = last; // reset the last
18912             view.focusRow(rowIndex);
18913         }else{
18914             var isSelected = this.isSelected(rowIndex);
18915             if(e.button !== 0 && isSelected){
18916                 view.focusRow(rowIndex);
18917             }else if(e.ctrlKey && isSelected){
18918                 this.deselectRow(rowIndex);
18919             }else if(!isSelected){
18920                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18921                 view.focusRow(rowIndex);
18922             }
18923         }
18924         this.fireEvent("afterselectionchange", this);
18925     },
18926     // private
18927     handleDragableRowClick :  function(grid, rowIndex, e) 
18928     {
18929         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18930             this.selectRow(rowIndex, false);
18931             grid.view.focusRow(rowIndex);
18932              this.fireEvent("afterselectionchange", this);
18933         }
18934     },
18935     
18936     /**
18937      * Selects multiple rows.
18938      * @param {Array} rows Array of the indexes of the row to select
18939      * @param {Boolean} keepExisting (optional) True to keep existing selections
18940      */
18941     selectRows : function(rows, keepExisting){
18942         if(!keepExisting){
18943             this.clearSelections();
18944         }
18945         for(var i = 0, len = rows.length; i < len; i++){
18946             this.selectRow(rows[i], true);
18947         }
18948     },
18949
18950     /**
18951      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18952      * @param {Number} startRow The index of the first row in the range
18953      * @param {Number} endRow The index of the last row in the range
18954      * @param {Boolean} keepExisting (optional) True to retain existing selections
18955      */
18956     selectRange : function(startRow, endRow, keepExisting){
18957         if(this.locked) return;
18958         if(!keepExisting){
18959             this.clearSelections();
18960         }
18961         if(startRow <= endRow){
18962             for(var i = startRow; i <= endRow; i++){
18963                 this.selectRow(i, true);
18964             }
18965         }else{
18966             for(var i = startRow; i >= endRow; i--){
18967                 this.selectRow(i, true);
18968             }
18969         }
18970     },
18971
18972     /**
18973      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18974      * @param {Number} startRow The index of the first row in the range
18975      * @param {Number} endRow The index of the last row in the range
18976      */
18977     deselectRange : function(startRow, endRow, preventViewNotify){
18978         if(this.locked) return;
18979         for(var i = startRow; i <= endRow; i++){
18980             this.deselectRow(i, preventViewNotify);
18981         }
18982     },
18983
18984     /**
18985      * Selects a row.
18986      * @param {Number} row The index of the row to select
18987      * @param {Boolean} keepExisting (optional) True to keep existing selections
18988      */
18989     selectRow : function(index, keepExisting, preventViewNotify){
18990         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18991         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18992             if(!keepExisting || this.singleSelect){
18993                 this.clearSelections();
18994             }
18995             var r = this.grid.dataSource.getAt(index);
18996             this.selections.add(r);
18997             this.last = this.lastActive = index;
18998             if(!preventViewNotify){
18999                 this.grid.getView().onRowSelect(index);
19000             }
19001             this.fireEvent("rowselect", this, index, r);
19002             this.fireEvent("selectionchange", this);
19003         }
19004     },
19005
19006     /**
19007      * Deselects a row.
19008      * @param {Number} row The index of the row to deselect
19009      */
19010     deselectRow : function(index, preventViewNotify){
19011         if(this.locked) return;
19012         if(this.last == index){
19013             this.last = false;
19014         }
19015         if(this.lastActive == index){
19016             this.lastActive = false;
19017         }
19018         var r = this.grid.dataSource.getAt(index);
19019         this.selections.remove(r);
19020         if(!preventViewNotify){
19021             this.grid.getView().onRowDeselect(index);
19022         }
19023         this.fireEvent("rowdeselect", this, index);
19024         this.fireEvent("selectionchange", this);
19025     },
19026
19027     // private
19028     restoreLast : function(){
19029         if(this._last){
19030             this.last = this._last;
19031         }
19032     },
19033
19034     // private
19035     acceptsNav : function(row, col, cm){
19036         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19037     },
19038
19039     // private
19040     onEditorKey : function(field, e){
19041         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19042         if(k == e.TAB){
19043             e.stopEvent();
19044             ed.completeEdit();
19045             if(e.shiftKey){
19046                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19047             }else{
19048                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19049             }
19050         }else if(k == e.ENTER && !e.ctrlKey){
19051             e.stopEvent();
19052             ed.completeEdit();
19053             if(e.shiftKey){
19054                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19055             }else{
19056                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19057             }
19058         }else if(k == e.ESC){
19059             ed.cancelEdit();
19060         }
19061         if(newCell){
19062             g.startEditing(newCell[0], newCell[1]);
19063         }
19064     }
19065 });/*
19066  * Based on:
19067  * Ext JS Library 1.1.1
19068  * Copyright(c) 2006-2007, Ext JS, LLC.
19069  *
19070  * Originally Released Under LGPL - original licence link has changed is not relivant.
19071  *
19072  * Fork - LGPL
19073  * <script type="text/javascript">
19074  */
19075  
19076 /**
19077  * @class Roo.bootstrap.PagingToolbar
19078  * @extends Roo.Row
19079  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19080  * @constructor
19081  * Create a new PagingToolbar
19082  * @param {Object} config The config object
19083  */
19084 Roo.bootstrap.PagingToolbar = function(config)
19085 {
19086     // old args format still supported... - xtype is prefered..
19087         // created from xtype...
19088     var ds = config.dataSource;
19089     this.toolbarItems = [];
19090     if (config.items) {
19091         this.toolbarItems = config.items;
19092 //        config.items = [];
19093     }
19094     
19095     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19096     this.ds = ds;
19097     this.cursor = 0;
19098     if (ds) { 
19099         this.bind(ds);
19100     }
19101     
19102     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19103     
19104 };
19105
19106 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19107     /**
19108      * @cfg {Roo.data.Store} dataSource
19109      * The underlying data store providing the paged data
19110      */
19111     /**
19112      * @cfg {String/HTMLElement/Element} container
19113      * container The id or element that will contain the toolbar
19114      */
19115     /**
19116      * @cfg {Boolean} displayInfo
19117      * True to display the displayMsg (defaults to false)
19118      */
19119     /**
19120      * @cfg {Number} pageSize
19121      * The number of records to display per page (defaults to 20)
19122      */
19123     pageSize: 20,
19124     /**
19125      * @cfg {String} displayMsg
19126      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19127      */
19128     displayMsg : 'Displaying {0} - {1} of {2}',
19129     /**
19130      * @cfg {String} emptyMsg
19131      * The message to display when no records are found (defaults to "No data to display")
19132      */
19133     emptyMsg : 'No data to display',
19134     /**
19135      * Customizable piece of the default paging text (defaults to "Page")
19136      * @type String
19137      */
19138     beforePageText : "Page",
19139     /**
19140      * Customizable piece of the default paging text (defaults to "of %0")
19141      * @type String
19142      */
19143     afterPageText : "of {0}",
19144     /**
19145      * Customizable piece of the default paging text (defaults to "First Page")
19146      * @type String
19147      */
19148     firstText : "First Page",
19149     /**
19150      * Customizable piece of the default paging text (defaults to "Previous Page")
19151      * @type String
19152      */
19153     prevText : "Previous Page",
19154     /**
19155      * Customizable piece of the default paging text (defaults to "Next Page")
19156      * @type String
19157      */
19158     nextText : "Next Page",
19159     /**
19160      * Customizable piece of the default paging text (defaults to "Last Page")
19161      * @type String
19162      */
19163     lastText : "Last Page",
19164     /**
19165      * Customizable piece of the default paging text (defaults to "Refresh")
19166      * @type String
19167      */
19168     refreshText : "Refresh",
19169
19170     buttons : false,
19171     // private
19172     onRender : function(ct, position) 
19173     {
19174         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19175         this.navgroup.parentId = this.id;
19176         this.navgroup.onRender(this.el, null);
19177         // add the buttons to the navgroup
19178         
19179         if(this.displayInfo){
19180             Roo.log(this.el.select('ul.navbar-nav',true).first());
19181             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19182             this.displayEl = this.el.select('.x-paging-info', true).first();
19183 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19184 //            this.displayEl = navel.el.select('span',true).first();
19185         }
19186         
19187         var _this = this;
19188         
19189         if(this.buttons){
19190             Roo.each(_this.buttons, function(e){
19191                Roo.factory(e).onRender(_this.el, null);
19192             });
19193         }
19194             
19195         Roo.each(_this.toolbarItems, function(e) {
19196             _this.navgroup.addItem(e);
19197         });
19198         
19199         this.first = this.navgroup.addItem({
19200             tooltip: this.firstText,
19201             cls: "prev",
19202             icon : 'fa fa-backward',
19203             disabled: true,
19204             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19205         });
19206         
19207         this.prev =  this.navgroup.addItem({
19208             tooltip: this.prevText,
19209             cls: "prev",
19210             icon : 'fa fa-step-backward',
19211             disabled: true,
19212             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19213         });
19214     //this.addSeparator();
19215         
19216         
19217         var field = this.navgroup.addItem( {
19218             tagtype : 'span',
19219             cls : 'x-paging-position',
19220             
19221             html : this.beforePageText  +
19222                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19223                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19224          } ); //?? escaped?
19225         
19226         this.field = field.el.select('input', true).first();
19227         this.field.on("keydown", this.onPagingKeydown, this);
19228         this.field.on("focus", function(){this.dom.select();});
19229     
19230     
19231         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19232         //this.field.setHeight(18);
19233         //this.addSeparator();
19234         this.next = this.navgroup.addItem({
19235             tooltip: this.nextText,
19236             cls: "next",
19237             html : ' <i class="fa fa-step-forward">',
19238             disabled: true,
19239             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19240         });
19241         this.last = this.navgroup.addItem({
19242             tooltip: this.lastText,
19243             icon : 'fa fa-forward',
19244             cls: "next",
19245             disabled: true,
19246             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19247         });
19248     //this.addSeparator();
19249         this.loading = this.navgroup.addItem({
19250             tooltip: this.refreshText,
19251             icon: 'fa fa-refresh',
19252             
19253             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19254         });
19255
19256     },
19257
19258     // private
19259     updateInfo : function(){
19260         if(this.displayEl){
19261             var count = this.ds.getCount();
19262             var msg = count == 0 ?
19263                 this.emptyMsg :
19264                 String.format(
19265                     this.displayMsg,
19266                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19267                 );
19268             this.displayEl.update(msg);
19269         }
19270     },
19271
19272     // private
19273     onLoad : function(ds, r, o){
19274        this.cursor = o.params ? o.params.start : 0;
19275        var d = this.getPageData(),
19276             ap = d.activePage,
19277             ps = d.pages;
19278         
19279        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19280        this.field.dom.value = ap;
19281        this.first.setDisabled(ap == 1);
19282        this.prev.setDisabled(ap == 1);
19283        this.next.setDisabled(ap == ps);
19284        this.last.setDisabled(ap == ps);
19285        this.loading.enable();
19286        this.updateInfo();
19287     },
19288
19289     // private
19290     getPageData : function(){
19291         var total = this.ds.getTotalCount();
19292         return {
19293             total : total,
19294             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19295             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19296         };
19297     },
19298
19299     // private
19300     onLoadError : function(){
19301         this.loading.enable();
19302     },
19303
19304     // private
19305     onPagingKeydown : function(e){
19306         var k = e.getKey();
19307         var d = this.getPageData();
19308         if(k == e.RETURN){
19309             var v = this.field.dom.value, pageNum;
19310             if(!v || isNaN(pageNum = parseInt(v, 10))){
19311                 this.field.dom.value = d.activePage;
19312                 return;
19313             }
19314             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19315             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19316             e.stopEvent();
19317         }
19318         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))
19319         {
19320           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19321           this.field.dom.value = pageNum;
19322           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19323           e.stopEvent();
19324         }
19325         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19326         {
19327           var v = this.field.dom.value, pageNum; 
19328           var increment = (e.shiftKey) ? 10 : 1;
19329           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19330             increment *= -1;
19331           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19332             this.field.dom.value = d.activePage;
19333             return;
19334           }
19335           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19336           {
19337             this.field.dom.value = parseInt(v, 10) + increment;
19338             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19339             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19340           }
19341           e.stopEvent();
19342         }
19343     },
19344
19345     // private
19346     beforeLoad : function(){
19347         if(this.loading){
19348             this.loading.disable();
19349         }
19350     },
19351
19352     // private
19353     onClick : function(which){
19354         var ds = this.ds;
19355         if (!ds) {
19356             return;
19357         }
19358         switch(which){
19359             case "first":
19360                 ds.load({params:{start: 0, limit: this.pageSize}});
19361             break;
19362             case "prev":
19363                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19364             break;
19365             case "next":
19366                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19367             break;
19368             case "last":
19369                 var total = ds.getTotalCount();
19370                 var extra = total % this.pageSize;
19371                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19372                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19373             break;
19374             case "refresh":
19375                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19376             break;
19377         }
19378     },
19379
19380     /**
19381      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19382      * @param {Roo.data.Store} store The data store to unbind
19383      */
19384     unbind : function(ds){
19385         ds.un("beforeload", this.beforeLoad, this);
19386         ds.un("load", this.onLoad, this);
19387         ds.un("loadexception", this.onLoadError, this);
19388         ds.un("remove", this.updateInfo, this);
19389         ds.un("add", this.updateInfo, this);
19390         this.ds = undefined;
19391     },
19392
19393     /**
19394      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19395      * @param {Roo.data.Store} store The data store to bind
19396      */
19397     bind : function(ds){
19398         ds.on("beforeload", this.beforeLoad, this);
19399         ds.on("load", this.onLoad, this);
19400         ds.on("loadexception", this.onLoadError, this);
19401         ds.on("remove", this.updateInfo, this);
19402         ds.on("add", this.updateInfo, this);
19403         this.ds = ds;
19404     }
19405 });/*
19406  * - LGPL
19407  *
19408  * element
19409  * 
19410  */
19411
19412 /**
19413  * @class Roo.bootstrap.MessageBar
19414  * @extends Roo.bootstrap.Component
19415  * Bootstrap MessageBar class
19416  * @cfg {String} html contents of the MessageBar
19417  * @cfg {String} weight (info | success | warning | danger) default info
19418  * @cfg {String} beforeClass insert the bar before the given class
19419  * @cfg {Boolean} closable (true | false) default false
19420  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19421  * 
19422  * @constructor
19423  * Create a new Element
19424  * @param {Object} config The config object
19425  */
19426
19427 Roo.bootstrap.MessageBar = function(config){
19428     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19429 };
19430
19431 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19432     
19433     html: '',
19434     weight: 'info',
19435     closable: false,
19436     fixed: false,
19437     beforeClass: 'bootstrap-sticky-wrap',
19438     
19439     getAutoCreate : function(){
19440         
19441         var cfg = {
19442             tag: 'div',
19443             cls: 'alert alert-dismissable alert-' + this.weight,
19444             cn: [
19445                 {
19446                     tag: 'span',
19447                     cls: 'message',
19448                     html: this.html || ''
19449                 }
19450             ]
19451         }
19452         
19453         if(this.fixed){
19454             cfg.cls += ' alert-messages-fixed';
19455         }
19456         
19457         if(this.closable){
19458             cfg.cn.push({
19459                 tag: 'button',
19460                 cls: 'close',
19461                 html: 'x'
19462             });
19463         }
19464         
19465         return cfg;
19466     },
19467     
19468     onRender : function(ct, position)
19469     {
19470         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19471         
19472         if(!this.el){
19473             var cfg = Roo.apply({},  this.getAutoCreate());
19474             cfg.id = Roo.id();
19475             
19476             if (this.cls) {
19477                 cfg.cls += ' ' + this.cls;
19478             }
19479             if (this.style) {
19480                 cfg.style = this.style;
19481             }
19482             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19483             
19484             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19485         }
19486         
19487         this.el.select('>button.close').on('click', this.hide, this);
19488         
19489     },
19490     
19491     show : function()
19492     {
19493         if (!this.rendered) {
19494             this.render();
19495         }
19496         
19497         this.el.show();
19498         
19499         this.fireEvent('show', this);
19500         
19501     },
19502     
19503     hide : function()
19504     {
19505         if (!this.rendered) {
19506             this.render();
19507         }
19508         
19509         this.el.hide();
19510         
19511         this.fireEvent('hide', this);
19512     },
19513     
19514     update : function()
19515     {
19516 //        var e = this.el.dom.firstChild;
19517 //        
19518 //        if(this.closable){
19519 //            e = e.nextSibling;
19520 //        }
19521 //        
19522 //        e.data = this.html || '';
19523
19524         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19525     }
19526    
19527 });
19528
19529  
19530
19531      /*
19532  * - LGPL
19533  *
19534  * Graph
19535  * 
19536  */
19537
19538
19539 /**
19540  * @class Roo.bootstrap.Graph
19541  * @extends Roo.bootstrap.Component
19542  * Bootstrap Graph class
19543 > Prameters
19544  -sm {number} sm 4
19545  -md {number} md 5
19546  @cfg {String} graphtype  bar | vbar | pie
19547  @cfg {number} g_x coodinator | centre x (pie)
19548  @cfg {number} g_y coodinator | centre y (pie)
19549  @cfg {number} g_r radius (pie)
19550  @cfg {number} g_height height of the chart (respected by all elements in the set)
19551  @cfg {number} g_width width of the chart (respected by all elements in the set)
19552  @cfg {Object} title The title of the chart
19553     
19554  -{Array}  values
19555  -opts (object) options for the chart 
19556      o {
19557      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19558      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19559      o vgutter (number)
19560      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.
19561      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19562      o to
19563      o stretch (boolean)
19564      o }
19565  -opts (object) options for the pie
19566      o{
19567      o cut
19568      o startAngle (number)
19569      o endAngle (number)
19570      } 
19571  *
19572  * @constructor
19573  * Create a new Input
19574  * @param {Object} config The config object
19575  */
19576
19577 Roo.bootstrap.Graph = function(config){
19578     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19579     
19580     this.addEvents({
19581         // img events
19582         /**
19583          * @event click
19584          * The img click event for the img.
19585          * @param {Roo.EventObject} e
19586          */
19587         "click" : true
19588     });
19589 };
19590
19591 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19592     
19593     sm: 4,
19594     md: 5,
19595     graphtype: 'bar',
19596     g_height: 250,
19597     g_width: 400,
19598     g_x: 50,
19599     g_y: 50,
19600     g_r: 30,
19601     opts:{
19602         //g_colors: this.colors,
19603         g_type: 'soft',
19604         g_gutter: '20%'
19605
19606     },
19607     title : false,
19608
19609     getAutoCreate : function(){
19610         
19611         var cfg = {
19612             tag: 'div',
19613             html : null
19614         }
19615         
19616         
19617         return  cfg;
19618     },
19619
19620     onRender : function(ct,position){
19621         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19622         this.raphael = Raphael(this.el.dom);
19623         
19624                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19625                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19626                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19627                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19628                 /*
19629                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19630                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19631                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19632                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19633                 
19634                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19635                 r.barchart(330, 10, 300, 220, data1);
19636                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19637                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19638                 */
19639                 
19640                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19641                 // r.barchart(30, 30, 560, 250,  xdata, {
19642                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19643                 //     axis : "0 0 1 1",
19644                 //     axisxlabels :  xdata
19645                 //     //yvalues : cols,
19646                    
19647                 // });
19648 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19649 //        
19650 //        this.load(null,xdata,{
19651 //                axis : "0 0 1 1",
19652 //                axisxlabels :  xdata
19653 //                });
19654
19655     },
19656
19657     load : function(graphtype,xdata,opts){
19658         this.raphael.clear();
19659         if(!graphtype) {
19660             graphtype = this.graphtype;
19661         }
19662         if(!opts){
19663             opts = this.opts;
19664         }
19665         var r = this.raphael,
19666             fin = function () {
19667                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19668             },
19669             fout = function () {
19670                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19671             },
19672             pfin = function() {
19673                 this.sector.stop();
19674                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19675
19676                 if (this.label) {
19677                     this.label[0].stop();
19678                     this.label[0].attr({ r: 7.5 });
19679                     this.label[1].attr({ "font-weight": 800 });
19680                 }
19681             },
19682             pfout = function() {
19683                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19684
19685                 if (this.label) {
19686                     this.label[0].animate({ r: 5 }, 500, "bounce");
19687                     this.label[1].attr({ "font-weight": 400 });
19688                 }
19689             };
19690
19691         switch(graphtype){
19692             case 'bar':
19693                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19694                 break;
19695             case 'hbar':
19696                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19697                 break;
19698             case 'pie':
19699 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19700 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19701 //            
19702                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19703                 
19704                 break;
19705
19706         }
19707         
19708         if(this.title){
19709             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19710         }
19711         
19712     },
19713     
19714     setTitle: function(o)
19715     {
19716         this.title = o;
19717     },
19718     
19719     initEvents: function() {
19720         
19721         if(!this.href){
19722             this.el.on('click', this.onClick, this);
19723         }
19724     },
19725     
19726     onClick : function(e)
19727     {
19728         Roo.log('img onclick');
19729         this.fireEvent('click', this, e);
19730     }
19731    
19732 });
19733
19734  
19735 /*
19736  * - LGPL
19737  *
19738  * numberBox
19739  * 
19740  */
19741 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19742
19743 /**
19744  * @class Roo.bootstrap.dash.NumberBox
19745  * @extends Roo.bootstrap.Component
19746  * Bootstrap NumberBox class
19747  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19748  * @cfg {String} headline Box headline
19749  * @cfg {String} content Box content
19750  * @cfg {String} icon Box icon
19751  * @cfg {String} footer Footer text
19752  * @cfg {String} fhref Footer href
19753  * 
19754  * @constructor
19755  * Create a new NumberBox
19756  * @param {Object} config The config object
19757  */
19758
19759
19760 Roo.bootstrap.dash.NumberBox = function(config){
19761     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19762     
19763 };
19764
19765 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19766     
19767     bgcolor : 'aqua',
19768     headline : '',
19769     content : '',
19770     icon : '',
19771     footer : '',
19772     fhref : '',
19773     ficon : '',
19774     
19775     getAutoCreate : function(){
19776         
19777         var cfg = {
19778             tag : 'div',
19779             cls : 'small-box bg-' + this.bgcolor,
19780             cn : [
19781                 {
19782                     tag : 'div',
19783                     cls : 'inner',
19784                     cn :[
19785                         {
19786                             tag : 'h3',
19787                             cls : 'roo-headline',
19788                             html : this.headline
19789                         },
19790                         {
19791                             tag : 'p',
19792                             cls : 'roo-content',
19793                             html : this.content
19794                         }
19795                     ]
19796                 }
19797             ]
19798         }
19799         
19800         if(this.icon){
19801             cfg.cn.push({
19802                 tag : 'div',
19803                 cls : 'icon',
19804                 cn :[
19805                     {
19806                         tag : 'i',
19807                         cls : 'ion ' + this.icon
19808                     }
19809                 ]
19810             });
19811         }
19812         
19813         if(this.footer){
19814             var footer = {
19815                 tag : 'a',
19816                 cls : 'small-box-footer',
19817                 href : this.fhref || '#',
19818                 html : this.footer
19819             };
19820             
19821             cfg.cn.push(footer);
19822             
19823         }
19824         
19825         return  cfg;
19826     },
19827
19828     onRender : function(ct,position){
19829         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19830
19831
19832        
19833                 
19834     },
19835
19836     setHeadline: function (value)
19837     {
19838         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19839     },
19840     
19841     setFooter: function (value, href)
19842     {
19843         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19844         
19845         if(href){
19846             this.el.select('a.small-box-footer',true).first().attr('href', href);
19847         }
19848         
19849     },
19850
19851     setContent: function (value)
19852     {
19853         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19854     },
19855
19856     initEvents: function() 
19857     {   
19858         
19859     }
19860     
19861 });
19862
19863  
19864 /*
19865  * - LGPL
19866  *
19867  * TabBox
19868  * 
19869  */
19870 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19871
19872 /**
19873  * @class Roo.bootstrap.dash.TabBox
19874  * @extends Roo.bootstrap.Component
19875  * Bootstrap TabBox class
19876  * @cfg {String} title Title of the TabBox
19877  * @cfg {String} icon Icon of the TabBox
19878  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19879  * 
19880  * @constructor
19881  * Create a new TabBox
19882  * @param {Object} config The config object
19883  */
19884
19885
19886 Roo.bootstrap.dash.TabBox = function(config){
19887     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19888     this.addEvents({
19889         // raw events
19890         /**
19891          * @event addpane
19892          * When a pane is added
19893          * @param {Roo.bootstrap.dash.TabPane} pane
19894          */
19895         "addpane" : true
19896          
19897     });
19898 };
19899
19900 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19901
19902     title : '',
19903     icon : false,
19904     showtabs : true,
19905     
19906     getChildContainer : function()
19907     {
19908         return this.el.select('.tab-content', true).first();
19909     },
19910     
19911     getAutoCreate : function(){
19912         
19913         var header = {
19914             tag: 'li',
19915             cls: 'pull-left header',
19916             html: this.title,
19917             cn : []
19918         };
19919         
19920         if(this.icon){
19921             header.cn.push({
19922                 tag: 'i',
19923                 cls: 'fa ' + this.icon
19924             });
19925         }
19926         
19927         
19928         var cfg = {
19929             tag: 'div',
19930             cls: 'nav-tabs-custom',
19931             cn: [
19932                 {
19933                     tag: 'ul',
19934                     cls: 'nav nav-tabs pull-right',
19935                     cn: [
19936                         header
19937                     ]
19938                 },
19939                 {
19940                     tag: 'div',
19941                     cls: 'tab-content no-padding',
19942                     cn: []
19943                 }
19944             ]
19945         }
19946
19947         return  cfg;
19948     },
19949     initEvents : function()
19950     {
19951         //Roo.log('add add pane handler');
19952         this.on('addpane', this.onAddPane, this);
19953     },
19954      /**
19955      * Updates the box title
19956      * @param {String} html to set the title to.
19957      */
19958     setTitle : function(value)
19959     {
19960         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19961     },
19962     onAddPane : function(pane)
19963     {
19964         //Roo.log('addpane');
19965         //Roo.log(pane);
19966         // tabs are rendere left to right..
19967         if(!this.showtabs){
19968             return;
19969         }
19970         
19971         var ctr = this.el.select('.nav-tabs', true).first();
19972          
19973          
19974         var existing = ctr.select('.nav-tab',true);
19975         var qty = existing.getCount();;
19976         
19977         
19978         var tab = ctr.createChild({
19979             tag : 'li',
19980             cls : 'nav-tab' + (qty ? '' : ' active'),
19981             cn : [
19982                 {
19983                     tag : 'a',
19984                     href:'#',
19985                     html : pane.title
19986                 }
19987             ]
19988         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19989         pane.tab = tab;
19990         
19991         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19992         if (!qty) {
19993             pane.el.addClass('active');
19994         }
19995         
19996                 
19997     },
19998     onTabClick : function(ev,un,ob,pane)
19999     {
20000         //Roo.log('tab - prev default');
20001         ev.preventDefault();
20002         
20003         
20004         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20005         pane.tab.addClass('active');
20006         //Roo.log(pane.title);
20007         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20008         // technically we should have a deactivate event.. but maybe add later.
20009         // and it should not de-activate the selected tab...
20010         
20011         pane.el.addClass('active');
20012         pane.fireEvent('activate');
20013         
20014         
20015     }
20016     
20017     
20018 });
20019
20020  
20021 /*
20022  * - LGPL
20023  *
20024  * Tab pane
20025  * 
20026  */
20027 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20028 /**
20029  * @class Roo.bootstrap.TabPane
20030  * @extends Roo.bootstrap.Component
20031  * Bootstrap TabPane class
20032  * @cfg {Boolean} active (false | true) Default false
20033  * @cfg {String} title title of panel
20034
20035  * 
20036  * @constructor
20037  * Create a new TabPane
20038  * @param {Object} config The config object
20039  */
20040
20041 Roo.bootstrap.dash.TabPane = function(config){
20042     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20043     
20044     this.addEvents({
20045         // raw events
20046         /**
20047          * @event activate
20048          * When a pane is activated
20049          * @param {Roo.bootstrap.dash.TabPane} pane
20050          */
20051         "activate" : true
20052          
20053     });
20054 };
20055
20056 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20057     
20058     active : false,
20059     title : '',
20060     
20061     // the tabBox that this is attached to.
20062     tab : false,
20063      
20064     getAutoCreate : function() 
20065     {
20066         var cfg = {
20067             tag: 'div',
20068             cls: 'tab-pane'
20069         }
20070         
20071         if(this.active){
20072             cfg.cls += ' active';
20073         }
20074         
20075         return cfg;
20076     },
20077     initEvents  : function()
20078     {
20079         //Roo.log('trigger add pane handler');
20080         this.parent().fireEvent('addpane', this)
20081     },
20082     
20083      /**
20084      * Updates the tab title 
20085      * @param {String} html to set the title to.
20086      */
20087     setTitle: function(str)
20088     {
20089         if (!this.tab) {
20090             return;
20091         }
20092         this.title = str;
20093         this.tab.select('a', true).first().dom.innerHTML = str;
20094         
20095     }
20096     
20097     
20098     
20099 });
20100
20101  
20102
20103
20104  /*
20105  * - LGPL
20106  *
20107  * menu
20108  * 
20109  */
20110 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20111
20112 /**
20113  * @class Roo.bootstrap.menu.Menu
20114  * @extends Roo.bootstrap.Component
20115  * Bootstrap Menu class - container for Menu
20116  * @cfg {String} html Text of the menu
20117  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20118  * @cfg {String} icon Font awesome icon
20119  * @cfg {String} pos Menu align to (top | bottom) default bottom
20120  * 
20121  * 
20122  * @constructor
20123  * Create a new Menu
20124  * @param {Object} config The config object
20125  */
20126
20127
20128 Roo.bootstrap.menu.Menu = function(config){
20129     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20130     
20131     this.addEvents({
20132         /**
20133          * @event beforeshow
20134          * Fires before this menu is displayed
20135          * @param {Roo.bootstrap.menu.Menu} this
20136          */
20137         beforeshow : true,
20138         /**
20139          * @event beforehide
20140          * Fires before this menu is hidden
20141          * @param {Roo.bootstrap.menu.Menu} this
20142          */
20143         beforehide : true,
20144         /**
20145          * @event show
20146          * Fires after this menu is displayed
20147          * @param {Roo.bootstrap.menu.Menu} this
20148          */
20149         show : true,
20150         /**
20151          * @event hide
20152          * Fires after this menu is hidden
20153          * @param {Roo.bootstrap.menu.Menu} this
20154          */
20155         hide : true,
20156         /**
20157          * @event click
20158          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20159          * @param {Roo.bootstrap.menu.Menu} this
20160          * @param {Roo.EventObject} e
20161          */
20162         click : true
20163     });
20164     
20165 };
20166
20167 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20168     
20169     submenu : false,
20170     html : '',
20171     weight : 'default',
20172     icon : false,
20173     pos : 'bottom',
20174     
20175     
20176     getChildContainer : function() {
20177         if(this.isSubMenu){
20178             return this.el;
20179         }
20180         
20181         return this.el.select('ul.dropdown-menu', true).first();  
20182     },
20183     
20184     getAutoCreate : function()
20185     {
20186         var text = [
20187             {
20188                 tag : 'span',
20189                 cls : 'roo-menu-text',
20190                 html : this.html
20191             }
20192         ];
20193         
20194         if(this.icon){
20195             text.unshift({
20196                 tag : 'i',
20197                 cls : 'fa ' + this.icon
20198             })
20199         }
20200         
20201         
20202         var cfg = {
20203             tag : 'div',
20204             cls : 'btn-group',
20205             cn : [
20206                 {
20207                     tag : 'button',
20208                     cls : 'dropdown-button btn btn-' + this.weight,
20209                     cn : text
20210                 },
20211                 {
20212                     tag : 'button',
20213                     cls : 'dropdown-toggle btn btn-' + this.weight,
20214                     cn : [
20215                         {
20216                             tag : 'span',
20217                             cls : 'caret'
20218                         }
20219                     ]
20220                 },
20221                 {
20222                     tag : 'ul',
20223                     cls : 'dropdown-menu'
20224                 }
20225             ]
20226             
20227         };
20228         
20229         if(this.pos == 'top'){
20230             cfg.cls += ' dropup';
20231         }
20232         
20233         if(this.isSubMenu){
20234             cfg = {
20235                 tag : 'ul',
20236                 cls : 'dropdown-menu'
20237             }
20238         }
20239         
20240         return cfg;
20241     },
20242     
20243     onRender : function(ct, position)
20244     {
20245         this.isSubMenu = ct.hasClass('dropdown-submenu');
20246         
20247         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20248     },
20249     
20250     initEvents : function() 
20251     {
20252         if(this.isSubMenu){
20253             return;
20254         }
20255         
20256         this.hidden = true;
20257         
20258         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20259         this.triggerEl.on('click', this.onTriggerPress, this);
20260         
20261         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20262         this.buttonEl.on('click', this.onClick, this);
20263         
20264     },
20265     
20266     list : function()
20267     {
20268         if(this.isSubMenu){
20269             return this.el;
20270         }
20271         
20272         return this.el.select('ul.dropdown-menu', true).first();
20273     },
20274     
20275     onClick : function(e)
20276     {
20277         this.fireEvent("click", this, e);
20278     },
20279     
20280     onTriggerPress  : function(e)
20281     {   
20282         if (this.isVisible()) {
20283             this.hide();
20284         } else {
20285             this.show();
20286         }
20287     },
20288     
20289     isVisible : function(){
20290         return !this.hidden;
20291     },
20292     
20293     show : function()
20294     {
20295         this.fireEvent("beforeshow", this);
20296         
20297         this.hidden = false;
20298         this.el.addClass('open');
20299         
20300         Roo.get(document).on("mouseup", this.onMouseUp, this);
20301         
20302         this.fireEvent("show", this);
20303         
20304         
20305     },
20306     
20307     hide : function()
20308     {
20309         this.fireEvent("beforehide", this);
20310         
20311         this.hidden = true;
20312         this.el.removeClass('open');
20313         
20314         Roo.get(document).un("mouseup", this.onMouseUp);
20315         
20316         this.fireEvent("hide", this);
20317     },
20318     
20319     onMouseUp : function()
20320     {
20321         this.hide();
20322     }
20323     
20324 });
20325
20326  
20327  /*
20328  * - LGPL
20329  *
20330  * menu item
20331  * 
20332  */
20333 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20334
20335 /**
20336  * @class Roo.bootstrap.menu.Item
20337  * @extends Roo.bootstrap.Component
20338  * Bootstrap MenuItem class
20339  * @cfg {Boolean} submenu (true | false) default false
20340  * @cfg {String} html text of the item
20341  * @cfg {String} href the link
20342  * @cfg {Boolean} disable (true | false) default false
20343  * @cfg {Boolean} preventDefault (true | false) default true
20344  * @cfg {String} icon Font awesome icon
20345  * @cfg {String} pos Submenu align to (left | right) default right 
20346  * 
20347  * 
20348  * @constructor
20349  * Create a new Item
20350  * @param {Object} config The config object
20351  */
20352
20353
20354 Roo.bootstrap.menu.Item = function(config){
20355     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20356     this.addEvents({
20357         /**
20358          * @event mouseover
20359          * Fires when the mouse is hovering over this menu
20360          * @param {Roo.bootstrap.menu.Item} this
20361          * @param {Roo.EventObject} e
20362          */
20363         mouseover : true,
20364         /**
20365          * @event mouseout
20366          * Fires when the mouse exits this menu
20367          * @param {Roo.bootstrap.menu.Item} this
20368          * @param {Roo.EventObject} e
20369          */
20370         mouseout : true,
20371         // raw events
20372         /**
20373          * @event click
20374          * The raw click event for the entire grid.
20375          * @param {Roo.EventObject} e
20376          */
20377         click : true
20378     });
20379 };
20380
20381 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20382     
20383     submenu : false,
20384     href : '',
20385     html : '',
20386     preventDefault: true,
20387     disable : false,
20388     icon : false,
20389     pos : 'right',
20390     
20391     getAutoCreate : function()
20392     {
20393         var text = [
20394             {
20395                 tag : 'span',
20396                 cls : 'roo-menu-item-text',
20397                 html : this.html
20398             }
20399         ];
20400         
20401         if(this.icon){
20402             text.unshift({
20403                 tag : 'i',
20404                 cls : 'fa ' + this.icon
20405             })
20406         }
20407         
20408         var cfg = {
20409             tag : 'li',
20410             cn : [
20411                 {
20412                     tag : 'a',
20413                     href : this.href || '#',
20414                     cn : text
20415                 }
20416             ]
20417         };
20418         
20419         if(this.disable){
20420             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20421         }
20422         
20423         if(this.submenu){
20424             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20425             
20426             if(this.pos == 'left'){
20427                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20428             }
20429         }
20430         
20431         return cfg;
20432     },
20433     
20434     initEvents : function() 
20435     {
20436         this.el.on('mouseover', this.onMouseOver, this);
20437         this.el.on('mouseout', this.onMouseOut, this);
20438         
20439         this.el.select('a', true).first().on('click', this.onClick, this);
20440         
20441     },
20442     
20443     onClick : function(e)
20444     {
20445         if(this.preventDefault){
20446             e.preventDefault();
20447         }
20448         
20449         this.fireEvent("click", this, e);
20450     },
20451     
20452     onMouseOver : function(e)
20453     {
20454         if(this.submenu && this.pos == 'left'){
20455             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20456         }
20457         
20458         this.fireEvent("mouseover", this, e);
20459     },
20460     
20461     onMouseOut : function(e)
20462     {
20463         this.fireEvent("mouseout", this, e);
20464     }
20465 });
20466
20467  
20468
20469  /*
20470  * - LGPL
20471  *
20472  * menu separator
20473  * 
20474  */
20475 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20476
20477 /**
20478  * @class Roo.bootstrap.menu.Separator
20479  * @extends Roo.bootstrap.Component
20480  * Bootstrap Separator class
20481  * 
20482  * @constructor
20483  * Create a new Separator
20484  * @param {Object} config The config object
20485  */
20486
20487
20488 Roo.bootstrap.menu.Separator = function(config){
20489     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20490 };
20491
20492 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20493     
20494     getAutoCreate : function(){
20495         var cfg = {
20496             tag : 'li',
20497             cls: 'divider'
20498         };
20499         
20500         return cfg;
20501     }
20502    
20503 });
20504
20505  
20506
20507