roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192          skip_children = false;
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 
214                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
215                 // and are not displayed -this causes this to use up the wrong element when matching.
216                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
217                 
218                 
219                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
220                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
221                   
222                   
223                   
224                     cn.el = echild;
225                   //  Roo.log("GOT");
226                     //echild.dom.removeAttribute('xtype');
227                 } else {
228                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229                     Roo.log(self_cntr_el);
230                     Roo.log(echild);
231                     Roo.log(cn);
232                 }
233             }
234            
235             
236            
237             // if object has flexy:if - then it may or may not be rendered.
238             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
239                 // skip a flexy if element.
240                 Roo.log('skipping render');
241                 Roo.log(tree);
242                 if (!cn.el) {
243                     Roo.log('skipping all children');
244                     skip_children = true;
245                 }
246                 
247              } else {
248                  
249                 // actually if flexy:foreach is found, we really want to create 
250                 // multiple copies here...
251                 //Roo.log('render');
252                 //Roo.log(this[cntr]());
253                 cn.render(this[cntr](true));
254              }
255             // then add the element..
256         }
257         
258         
259         // handle the kids..
260         
261         var nitems = [];
262         /*
263         if (typeof (tree.menu) != 'undefined') {
264             tree.menu.parentType = cn.xtype;
265             tree.menu.triggerEl = cn.el;
266             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
267             
268         }
269         */
270         if (!tree.items || !tree.items.length) {
271             cn.items = nitems;
272             return cn;
273         }
274         var items = tree.items;
275         delete tree.items;
276         
277         //Roo.log(items.length);
278             // add the items..
279         if (!skip_children) {    
280             for(var i =0;i < items.length;i++) {
281                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
282             }
283         }
284         
285         cn.items = nitems;
286         
287         return cn;
288     }
289     
290     
291     
292     
293 });
294
295  /*
296  * - LGPL
297  *
298  * Body
299  * 
300  */
301
302 /**
303  * @class Roo.bootstrap.Body
304  * @extends Roo.bootstrap.Component
305  * Bootstrap Body class
306  * 
307  * @constructor
308  * Create a new body
309  * @param {Object} config The config object
310  */
311
312 Roo.bootstrap.Body = function(config){
313     Roo.bootstrap.Body.superclass.constructor.call(this, config);
314     this.el = Roo.get(document.body);
315     if (this.cls && this.cls.length) {
316         Roo.get(document.body).addClass(this.cls);
317     }
318 };
319
320 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
321       
322         autoCreate : {
323         cls: 'container'
324     },
325     onRender : function(ct, position)
326     {
327        /* Roo.log("Roo.bootstrap.Body - onRender");
328         if (this.cls && this.cls.length) {
329             Roo.get(document.body).addClass(this.cls);
330         }
331         // style??? xttr???
332         */
333     }
334     
335     
336  
337    
338 });
339
340  /*
341  * - LGPL
342  *
343  * button group
344  * 
345  */
346
347
348 /**
349  * @class Roo.bootstrap.ButtonGroup
350  * @extends Roo.bootstrap.Component
351  * Bootstrap ButtonGroup class
352  * @cfg {String} size lg | sm | xs (default empty normal)
353  * @cfg {String} align vertical | justified  (default none)
354  * @cfg {String} direction up | down (default down)
355  * @cfg {Boolean} toolbar false | true
356  * @cfg {Boolean} btn true | false
357  * 
358  * 
359  * @constructor
360  * Create a new Input
361  * @param {Object} config The config object
362  */
363
364 Roo.bootstrap.ButtonGroup = function(config){
365     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
366 };
367
368 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
369     
370     size: '',
371     align: '',
372     direction: '',
373     toolbar: false,
374     btn: true,
375
376     getAutoCreate : function(){
377         var cfg = {
378             cls: 'btn-group',
379             html : null
380         }
381         
382         cfg.html = this.html || cfg.html;
383         
384         if (this.toolbar) {
385             cfg = {
386                 cls: 'btn-toolbar',
387                 html: null
388             }
389             
390             return cfg;
391         }
392         
393         if (['vertical','justified'].indexOf(this.align)!==-1) {
394             cfg.cls = 'btn-group-' + this.align;
395             
396             if (this.align == 'justified') {
397                 console.log(this.items);
398             }
399         }
400         
401         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
402             cfg.cls += ' btn-group-' + this.size;
403         }
404         
405         if (this.direction == 'up') {
406             cfg.cls += ' dropup' ;
407         }
408         
409         return cfg;
410     }
411    
412 });
413
414  /*
415  * - LGPL
416  *
417  * button
418  * 
419  */
420
421 /**
422  * @class Roo.bootstrap.Button
423  * @extends Roo.bootstrap.Component
424  * Bootstrap Button class
425  * @cfg {String} html The button content
426  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
427  * @cfg {String} size empty | lg | sm | xs
428  * @cfg {String} tag empty | a | input | submit
429  * @cfg {String} href empty or href
430  * @cfg {Boolean} disabled false | true
431  * @cfg {Boolean} isClose false | true
432  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
433  * @cfg {String} badge text for badge
434  * @cfg {String} theme default (or empty) | glow
435  * @cfg {Boolean} inverse false | true
436  * @cfg {Boolean} toggle false | true
437  * @cfg {String} ontext text for on toggle state
438  * @cfg {String} offtext text for off toggle state
439  * @cfg {Boolean} defaulton true | false
440  * @cfg {Boolean} preventDefault (true | false) default true
441  * @cfg {Boolean} removeClass true | false remove the standard class..
442  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
443  * 
444  * @constructor
445  * Create a new button
446  * @param {Object} config The config object
447  */
448
449
450 Roo.bootstrap.Button = function(config){
451     Roo.bootstrap.Button.superclass.constructor.call(this, config);
452     this.addEvents({
453         // raw events
454         /**
455          * @event click
456          * When a butotn is pressed
457          * @param {Roo.EventObject} e
458          */
459         "click" : true,
460          /**
461          * @event toggle
462          * After the button has been toggles
463          * @param {Roo.EventObject} e
464          * @param {boolean} pressed (also available as button.pressed)
465          */
466         "toggle" : true
467     });
468 };
469
470 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
471     html: false,
472     active: false,
473     weight: '',
474     size: '',
475     tag: 'button',
476     href: '',
477     disabled: false,
478     isClose: false,
479     glyphicon: '',
480     badge: '',
481     theme: 'default',
482     inverse: false,
483     
484     toggle: false,
485     ontext: 'ON',
486     offtext: 'OFF',
487     defaulton: true,
488     preventDefault: true,
489     removeClass: false,
490     name: false,
491     target: false,
492     
493     
494     pressed : null,
495      
496     
497     getAutoCreate : function(){
498         
499         var cfg = {
500             tag : 'button',
501             cls : 'roo-button',
502             html: ''
503         };
504         
505         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
506             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
507             this.tag = 'button';
508         } else {
509             cfg.tag = this.tag;
510         }
511         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
512         
513         if (this.toggle == true) {
514             cfg={
515                 tag: 'div',
516                 cls: 'slider-frame roo-button',
517                 cn: [
518                     {
519                         tag: 'span',
520                         'data-on-text':'ON',
521                         'data-off-text':'OFF',
522                         cls: 'slider-button',
523                         html: this.offtext
524                     }
525                 ]
526             };
527             
528             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
529                 cfg.cls += ' '+this.weight;
530             }
531             
532             return cfg;
533         }
534         
535         if (this.isClose) {
536             cfg.cls += ' close';
537             
538             cfg["aria-hidden"] = true;
539             
540             cfg.html = "&times;";
541             
542             return cfg;
543         }
544         
545          
546         if (this.theme==='default') {
547             cfg.cls = 'btn roo-button';
548             
549             //if (this.parentType != 'Navbar') {
550             this.weight = this.weight.length ?  this.weight : 'default';
551             //}
552             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
553                 
554                 cfg.cls += ' btn-' + this.weight;
555             }
556         } else if (this.theme==='glow') {
557             
558             cfg.tag = 'a';
559             cfg.cls = 'btn-glow roo-button';
560             
561             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
562                 
563                 cfg.cls += ' ' + this.weight;
564             }
565         }
566    
567         
568         if (this.inverse) {
569             this.cls += ' inverse';
570         }
571         
572         
573         if (this.active) {
574             cfg.cls += ' active';
575         }
576         
577         if (this.disabled) {
578             cfg.disabled = 'disabled';
579         }
580         
581         if (this.items) {
582             Roo.log('changing to ul' );
583             cfg.tag = 'ul';
584             this.glyphicon = 'caret';
585         }
586         
587         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
588          
589         //gsRoo.log(this.parentType);
590         if (this.parentType === 'Navbar' && !this.parent().bar) {
591             Roo.log('changing to li?');
592             
593             cfg.tag = 'li';
594             
595             cfg.cls = '';
596             cfg.cn =  [{
597                 tag : 'a',
598                 cls : 'roo-button',
599                 html : this.html,
600                 href : this.href || '#'
601             }];
602             if (this.menu) {
603                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
604                 cfg.cls += ' dropdown';
605             }   
606             
607             delete cfg.html;
608             
609         }
610         
611        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
612         
613         if (this.glyphicon) {
614             cfg.html = ' ' + cfg.html;
615             
616             cfg.cn = [
617                 {
618                     tag: 'span',
619                     cls: 'glyphicon glyphicon-' + this.glyphicon
620                 }
621             ];
622         }
623         
624         if (this.badge) {
625             cfg.html += ' ';
626             
627             cfg.tag = 'a';
628             
629 //            cfg.cls='btn roo-button';
630             
631             cfg.href=this.href;
632             
633             var value = cfg.html;
634             
635             if(this.glyphicon){
636                 value = {
637                             tag: 'span',
638                             cls: 'glyphicon glyphicon-' + this.glyphicon,
639                             html: this.html
640                         };
641                 
642             }
643             
644             cfg.cn = [
645                 value,
646                 {
647                     tag: 'span',
648                     cls: 'badge',
649                     html: this.badge
650                 }
651             ];
652             
653             cfg.html='';
654         }
655         
656         if (this.menu) {
657             cfg.cls += ' dropdown';
658             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
659         }
660         
661         if (cfg.tag !== 'a' && this.href !== '') {
662             throw "Tag must be a to set href.";
663         } else if (this.href.length > 0) {
664             cfg.href = this.href;
665         }
666         
667         if(this.removeClass){
668             cfg.cls = '';
669         }
670         
671         if(this.target){
672             cfg.target = this.target;
673         }
674         
675         return cfg;
676     },
677     initEvents: function() {
678        // Roo.log('init events?');
679 //        Roo.log(this.el.dom);
680         // add the menu...
681         
682         if (typeof (this.menu) != 'undefined') {
683             this.menu.parentType = this.xtype;
684             this.menu.triggerEl = this.el;
685             this.addxtype(Roo.apply({}, this.menu));
686         }
687
688
689        if (this.el.hasClass('roo-button')) {
690             this.el.on('click', this.onClick, this);
691        } else {
692             this.el.select('.roo-button').on('click', this.onClick, this);
693        }
694        
695        if(this.removeClass){
696            this.el.on('click', this.onClick, this);
697        }
698        
699        this.el.enableDisplayMode();
700         
701     },
702     onClick : function(e)
703     {
704         if (this.disabled) {
705             return;
706         }
707         
708         Roo.log('button on click ');
709         if(this.preventDefault){
710             e.preventDefault();
711         }
712         if (this.pressed === true || this.pressed === false) {
713             this.pressed = !this.pressed;
714             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
715             this.fireEvent('toggle', this, e, this.pressed);
716         }
717         
718         
719         this.fireEvent('click', this, e);
720     },
721     
722     /**
723      * Enables this button
724      */
725     enable : function()
726     {
727         this.disabled = false;
728         this.el.removeClass('disabled');
729     },
730     
731     /**
732      * Disable this button
733      */
734     disable : function()
735     {
736         this.disabled = true;
737         this.el.addClass('disabled');
738     },
739      /**
740      * sets the active state on/off, 
741      * @param {Boolean} state (optional) Force a particular state
742      */
743     setActive : function(v) {
744         
745         this.el[v ? 'addClass' : 'removeClass']('active');
746     },
747      /**
748      * toggles the current active state 
749      */
750     toggleActive : function()
751     {
752        var active = this.el.hasClass('active');
753        this.setActive(!active);
754        
755         
756     },
757     setText : function(str)
758     {
759         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
760     },
761     getText : function()
762     {
763         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
764     },
765     hide: function() {
766        
767      
768         this.el.hide();   
769     },
770     show: function() {
771        
772         this.el.show();   
773     }
774     
775     
776 });
777
778  /*
779  * - LGPL
780  *
781  * column
782  * 
783  */
784
785 /**
786  * @class Roo.bootstrap.Column
787  * @extends Roo.bootstrap.Component
788  * Bootstrap Column class
789  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
790  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
791  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
792  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
793  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
794  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
795  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
796  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
797  *
798  * 
799  * @cfg {Boolean} hidden (true|false) hide the element
800  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
801  * @cfg {String} fa (ban|check|...) font awesome icon
802  * @cfg {Number} fasize (1|2|....) font awsome size
803
804  * @cfg {String} icon (info-sign|check|...) glyphicon name
805
806  * @cfg {String} html content of column.
807  * 
808  * @constructor
809  * Create a new Column
810  * @param {Object} config The config object
811  */
812
813 Roo.bootstrap.Column = function(config){
814     Roo.bootstrap.Column.superclass.constructor.call(this, config);
815 };
816
817 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
818     
819     xs: false,
820     sm: false,
821     md: false,
822     lg: false,
823     xsoff: false,
824     smoff: false,
825     mdoff: false,
826     lgoff: false,
827     html: '',
828     offset: 0,
829     alert: false,
830     fa: false,
831     icon : false,
832     hidden : false,
833     fasize : 1,
834     
835     getAutoCreate : function(){
836         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
837         
838         cfg = {
839             tag: 'div',
840             cls: 'column'
841         };
842         
843         var settings=this;
844         ['xs','sm','md','lg'].map(function(size){
845             //Roo.log( size + ':' + settings[size]);
846             
847             if (settings[size+'off'] !== false) {
848                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
849             }
850             
851             if (settings[size] === false) {
852                 return;
853             }
854             Roo.log(settings[size]);
855             if (!settings[size]) { // 0 = hidden
856                 cfg.cls += ' hidden-' + size;
857                 return;
858             }
859             cfg.cls += ' col-' + size + '-' + settings[size];
860             
861         });
862         
863         if (this.hidden) {
864             cfg.cls += ' hidden';
865         }
866         
867         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
868             cfg.cls +=' alert alert-' + this.alert;
869         }
870         
871         
872         if (this.html.length) {
873             cfg.html = this.html;
874         }
875         if (this.fa) {
876             var fasize = '';
877             if (this.fasize > 1) {
878                 fasize = ' fa-' + this.fasize + 'x';
879             }
880             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
881             
882             
883         }
884         if (this.icon) {
885             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
886         }
887         
888         return cfg;
889     }
890    
891 });
892
893  
894
895  /*
896  * - LGPL
897  *
898  * page container.
899  * 
900  */
901
902
903 /**
904  * @class Roo.bootstrap.Container
905  * @extends Roo.bootstrap.Component
906  * Bootstrap Container class
907  * @cfg {Boolean} jumbotron is it a jumbotron element
908  * @cfg {String} html content of element
909  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
910  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
911  * @cfg {String} header content of header (for panel)
912  * @cfg {String} footer content of footer (for panel)
913  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
914  * @cfg {String} tag (header|aside|section) type of HTML tag.
915  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
916  * @cfg {String} fa (ban|check|...) font awesome icon
917  * @cfg {String} icon (info-sign|check|...) glyphicon name
918  * @cfg {Boolean} hidden (true|false) hide the element
919
920  *     
921  * @constructor
922  * Create a new Container
923  * @param {Object} config The config object
924  */
925
926 Roo.bootstrap.Container = function(config){
927     Roo.bootstrap.Container.superclass.constructor.call(this, config);
928 };
929
930 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
931     
932     jumbotron : false,
933     well: '',
934     panel : '',
935     header: '',
936     footer : '',
937     sticky: '',
938     tag : false,
939     alert : false,
940     fa: false,
941     icon : false,
942   
943      
944     getChildContainer : function() {
945         
946         if(!this.el){
947             return false;
948         }
949         
950         if (this.panel.length) {
951             return this.el.select('.panel-body',true).first();
952         }
953         
954         return this.el;
955     },
956     
957     
958     getAutoCreate : function(){
959         
960         var cfg = {
961             tag : this.tag || 'div',
962             html : '',
963             cls : ''
964         };
965         if (this.jumbotron) {
966             cfg.cls = 'jumbotron';
967         }
968         
969         
970         
971         // - this is applied by the parent..
972         //if (this.cls) {
973         //    cfg.cls = this.cls + '';
974         //}
975         
976         if (this.sticky.length) {
977             
978             var bd = Roo.get(document.body);
979             if (!bd.hasClass('bootstrap-sticky')) {
980                 bd.addClass('bootstrap-sticky');
981                 Roo.select('html',true).setStyle('height', '100%');
982             }
983              
984             cfg.cls += 'bootstrap-sticky-' + this.sticky;
985         }
986         
987         
988         if (this.well.length) {
989             switch (this.well) {
990                 case 'lg':
991                 case 'sm':
992                     cfg.cls +=' well well-' +this.well;
993                     break;
994                 default:
995                     cfg.cls +=' well';
996                     break;
997             }
998         }
999         
1000         if (this.hidden) {
1001             cfg.cls += ' hidden';
1002         }
1003         
1004         
1005         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1006             cfg.cls +=' alert alert-' + this.alert;
1007         }
1008         
1009         var body = cfg;
1010         
1011         if (this.panel.length) {
1012             cfg.cls += ' panel panel-' + this.panel;
1013             cfg.cn = [];
1014             if (this.header.length) {
1015                 cfg.cn.push({
1016                     
1017                     cls : 'panel-heading',
1018                     cn : [{
1019                         tag: 'h3',
1020                         cls : 'panel-title',
1021                         html : this.header
1022                     }]
1023                     
1024                 });
1025             }
1026             body = false;
1027             cfg.cn.push({
1028                 cls : 'panel-body',
1029                 html : this.html
1030             });
1031             
1032             
1033             if (this.footer.length) {
1034                 cfg.cn.push({
1035                     cls : 'panel-footer',
1036                     html : this.footer
1037                     
1038                 });
1039             }
1040             
1041         }
1042         
1043         if (body) {
1044             body.html = this.html || cfg.html;
1045             // prefix with the icons..
1046             if (this.fa) {
1047                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1048             }
1049             if (this.icon) {
1050                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1051             }
1052             
1053             
1054         }
1055         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1056             cfg.cls =  'container';
1057         }
1058         
1059         return cfg;
1060     },
1061     
1062     titleEl : function()
1063     {
1064         if(!this.el || !this.panel.length || !this.header.length){
1065             return;
1066         }
1067         
1068         return this.el.select('.panel-title',true).first();
1069     },
1070     
1071     setTitle : function(v)
1072     {
1073         var titleEl = this.titleEl();
1074         
1075         if(!titleEl){
1076             return;
1077         }
1078         
1079         titleEl.dom.innerHTML = v;
1080     },
1081     
1082     getTitle : function()
1083     {
1084         
1085         var titleEl = this.titleEl();
1086         
1087         if(!titleEl){
1088             return '';
1089         }
1090         
1091         return titleEl.dom.innerHTML;
1092     }
1093    
1094 });
1095
1096  /*
1097  * - LGPL
1098  *
1099  * image
1100  * 
1101  */
1102
1103
1104 /**
1105  * @class Roo.bootstrap.Img
1106  * @extends Roo.bootstrap.Component
1107  * Bootstrap Img class
1108  * @cfg {Boolean} imgResponsive false | true
1109  * @cfg {String} border rounded | circle | thumbnail
1110  * @cfg {String} src image source
1111  * @cfg {String} alt image alternative text
1112  * @cfg {String} href a tag href
1113  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1114  * 
1115  * @constructor
1116  * Create a new Input
1117  * @param {Object} config The config object
1118  */
1119
1120 Roo.bootstrap.Img = function(config){
1121     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1122     
1123     this.addEvents({
1124         // img events
1125         /**
1126          * @event click
1127          * The img click event for the img.
1128          * @param {Roo.EventObject} e
1129          */
1130         "click" : true
1131     });
1132 };
1133
1134 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1135     
1136     imgResponsive: true,
1137     border: '',
1138     src: '',
1139     href: false,
1140     target: false,
1141
1142     getAutoCreate : function(){
1143         
1144         var cfg = {
1145             tag: 'img',
1146             cls: (this.imgResponsive) ? 'img-responsive' : '',
1147             html : null
1148         }
1149         
1150         cfg.html = this.html || cfg.html;
1151         
1152         cfg.src = this.src || cfg.src;
1153         
1154         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1155             cfg.cls += ' img-' + this.border;
1156         }
1157         
1158         if(this.alt){
1159             cfg.alt = this.alt;
1160         }
1161         
1162         if(this.href){
1163             var a = {
1164                 tag: 'a',
1165                 href: this.href,
1166                 cn: [
1167                     cfg
1168                 ]
1169             }
1170             
1171             if(this.target){
1172                 a.target = this.target;
1173             }
1174             
1175         }
1176         
1177         
1178         return (this.href) ? a : cfg;
1179     },
1180     
1181     initEvents: function() {
1182         
1183         if(!this.href){
1184             this.el.on('click', this.onClick, this);
1185         }
1186     },
1187     
1188     onClick : function(e)
1189     {
1190         Roo.log('img onclick');
1191         this.fireEvent('click', this, e);
1192     }
1193    
1194 });
1195
1196  /*
1197  * - LGPL
1198  *
1199  * image
1200  * 
1201  */
1202
1203
1204 /**
1205  * @class Roo.bootstrap.Link
1206  * @extends Roo.bootstrap.Component
1207  * Bootstrap Link Class
1208  * @cfg {String} alt image alternative text
1209  * @cfg {String} href a tag href
1210  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1211  * @cfg {String} html the content of the link.
1212  * @cfg {String} anchor name for the anchor link
1213
1214  * @cfg {Boolean} preventDefault (true | false) default false
1215
1216  * 
1217  * @constructor
1218  * Create a new Input
1219  * @param {Object} config The config object
1220  */
1221
1222 Roo.bootstrap.Link = function(config){
1223     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1224     
1225     this.addEvents({
1226         // img events
1227         /**
1228          * @event click
1229          * The img click event for the img.
1230          * @param {Roo.EventObject} e
1231          */
1232         "click" : true
1233     });
1234 };
1235
1236 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1237     
1238     href: false,
1239     target: false,
1240     preventDefault: false,
1241     anchor : false,
1242     alt : false,
1243
1244     getAutoCreate : function()
1245     {
1246         
1247         var cfg = {
1248             tag: 'a'
1249         };
1250         // anchor's do not require html/href...
1251         if (this.anchor === false) {
1252             cfg.html = this.html || 'html-missing';
1253             cfg.href = this.href || '#';
1254         } else {
1255             cfg.name = this.anchor;
1256             if (this.html !== false) {
1257                 cfg.html = this.html;
1258             }
1259             if (this.href !== false) {
1260                 cfg.href = this.href;
1261             }
1262         }
1263         
1264         if(this.alt !== false){
1265             cfg.alt = this.alt;
1266         }
1267         
1268         
1269         if(this.target !== false) {
1270             cfg.target = this.target;
1271         }
1272         
1273         return cfg;
1274     },
1275     
1276     initEvents: function() {
1277         
1278         if(!this.href || this.preventDefault){
1279             this.el.on('click', this.onClick, this);
1280         }
1281     },
1282     
1283     onClick : function(e)
1284     {
1285         if(this.preventDefault){
1286             e.preventDefault();
1287         }
1288         //Roo.log('img onclick');
1289         this.fireEvent('click', this, e);
1290     }
1291    
1292 });
1293
1294  /*
1295  * - LGPL
1296  *
1297  * header
1298  * 
1299  */
1300
1301 /**
1302  * @class Roo.bootstrap.Header
1303  * @extends Roo.bootstrap.Component
1304  * Bootstrap Header class
1305  * @cfg {String} html content of header
1306  * @cfg {Number} level (1|2|3|4|5|6) default 1
1307  * 
1308  * @constructor
1309  * Create a new Header
1310  * @param {Object} config The config object
1311  */
1312
1313
1314 Roo.bootstrap.Header  = function(config){
1315     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1316 };
1317
1318 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1319     
1320     //href : false,
1321     html : false,
1322     level : 1,
1323     
1324     
1325     
1326     getAutoCreate : function(){
1327         
1328         var cfg = {
1329             tag: 'h' + (1 *this.level),
1330             html: this.html || 'fill in html'
1331         } ;
1332         
1333         return cfg;
1334     }
1335    
1336 });
1337
1338  
1339
1340  /*
1341  * Based on:
1342  * Ext JS Library 1.1.1
1343  * Copyright(c) 2006-2007, Ext JS, LLC.
1344  *
1345  * Originally Released Under LGPL - original licence link has changed is not relivant.
1346  *
1347  * Fork - LGPL
1348  * <script type="text/javascript">
1349  */
1350  
1351 /**
1352  * @class Roo.bootstrap.MenuMgr
1353  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1354  * @singleton
1355  */
1356 Roo.bootstrap.MenuMgr = function(){
1357    var menus, active, groups = {}, attached = false, lastShow = new Date();
1358
1359    // private - called when first menu is created
1360    function init(){
1361        menus = {};
1362        active = new Roo.util.MixedCollection();
1363        Roo.get(document).addKeyListener(27, function(){
1364            if(active.length > 0){
1365                hideAll();
1366            }
1367        });
1368    }
1369
1370    // private
1371    function hideAll(){
1372        if(active && active.length > 0){
1373            var c = active.clone();
1374            c.each(function(m){
1375                m.hide();
1376            });
1377        }
1378    }
1379
1380    // private
1381    function onHide(m){
1382        active.remove(m);
1383        if(active.length < 1){
1384            Roo.get(document).un("mouseup", onMouseDown);
1385             
1386            attached = false;
1387        }
1388    }
1389
1390    // private
1391    function onShow(m){
1392        var last = active.last();
1393        lastShow = new Date();
1394        active.add(m);
1395        if(!attached){
1396           Roo.get(document).on("mouseup", onMouseDown);
1397            
1398            attached = true;
1399        }
1400        if(m.parentMenu){
1401           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1402           m.parentMenu.activeChild = m;
1403        }else if(last && last.isVisible()){
1404           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1405        }
1406    }
1407
1408    // private
1409    function onBeforeHide(m){
1410        if(m.activeChild){
1411            m.activeChild.hide();
1412        }
1413        if(m.autoHideTimer){
1414            clearTimeout(m.autoHideTimer);
1415            delete m.autoHideTimer;
1416        }
1417    }
1418
1419    // private
1420    function onBeforeShow(m){
1421        var pm = m.parentMenu;
1422        if(!pm && !m.allowOtherMenus){
1423            hideAll();
1424        }else if(pm && pm.activeChild && active != m){
1425            pm.activeChild.hide();
1426        }
1427    }
1428
1429    // private
1430    function onMouseDown(e){
1431         Roo.log("on MouseDown");
1432         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1433            hideAll();
1434         }
1435         
1436         
1437    }
1438
1439    // private
1440    function onBeforeCheck(mi, state){
1441        if(state){
1442            var g = groups[mi.group];
1443            for(var i = 0, l = g.length; i < l; i++){
1444                if(g[i] != mi){
1445                    g[i].setChecked(false);
1446                }
1447            }
1448        }
1449    }
1450
1451    return {
1452
1453        /**
1454         * Hides all menus that are currently visible
1455         */
1456        hideAll : function(){
1457             hideAll();  
1458        },
1459
1460        // private
1461        register : function(menu){
1462            if(!menus){
1463                init();
1464            }
1465            menus[menu.id] = menu;
1466            menu.on("beforehide", onBeforeHide);
1467            menu.on("hide", onHide);
1468            menu.on("beforeshow", onBeforeShow);
1469            menu.on("show", onShow);
1470            var g = menu.group;
1471            if(g && menu.events["checkchange"]){
1472                if(!groups[g]){
1473                    groups[g] = [];
1474                }
1475                groups[g].push(menu);
1476                menu.on("checkchange", onCheck);
1477            }
1478        },
1479
1480         /**
1481          * Returns a {@link Roo.menu.Menu} object
1482          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1483          * be used to generate and return a new Menu instance.
1484          */
1485        get : function(menu){
1486            if(typeof menu == "string"){ // menu id
1487                return menus[menu];
1488            }else if(menu.events){  // menu instance
1489                return menu;
1490            }
1491            /*else if(typeof menu.length == 'number'){ // array of menu items?
1492                return new Roo.bootstrap.Menu({items:menu});
1493            }else{ // otherwise, must be a config
1494                return new Roo.bootstrap.Menu(menu);
1495            }
1496            */
1497            return false;
1498        },
1499
1500        // private
1501        unregister : function(menu){
1502            delete menus[menu.id];
1503            menu.un("beforehide", onBeforeHide);
1504            menu.un("hide", onHide);
1505            menu.un("beforeshow", onBeforeShow);
1506            menu.un("show", onShow);
1507            var g = menu.group;
1508            if(g && menu.events["checkchange"]){
1509                groups[g].remove(menu);
1510                menu.un("checkchange", onCheck);
1511            }
1512        },
1513
1514        // private
1515        registerCheckable : function(menuItem){
1516            var g = menuItem.group;
1517            if(g){
1518                if(!groups[g]){
1519                    groups[g] = [];
1520                }
1521                groups[g].push(menuItem);
1522                menuItem.on("beforecheckchange", onBeforeCheck);
1523            }
1524        },
1525
1526        // private
1527        unregisterCheckable : function(menuItem){
1528            var g = menuItem.group;
1529            if(g){
1530                groups[g].remove(menuItem);
1531                menuItem.un("beforecheckchange", onBeforeCheck);
1532            }
1533        }
1534    };
1535 }();/*
1536  * - LGPL
1537  *
1538  * menu
1539  * 
1540  */
1541
1542 /**
1543  * @class Roo.bootstrap.Menu
1544  * @extends Roo.bootstrap.Component
1545  * Bootstrap Menu class - container for MenuItems
1546  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1547  * 
1548  * @constructor
1549  * Create a new Menu
1550  * @param {Object} config The config object
1551  */
1552
1553
1554 Roo.bootstrap.Menu = function(config){
1555     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1556     if (this.registerMenu) {
1557         Roo.bootstrap.MenuMgr.register(this);
1558     }
1559     this.addEvents({
1560         /**
1561          * @event beforeshow
1562          * Fires before this menu is displayed
1563          * @param {Roo.menu.Menu} this
1564          */
1565         beforeshow : true,
1566         /**
1567          * @event beforehide
1568          * Fires before this menu is hidden
1569          * @param {Roo.menu.Menu} this
1570          */
1571         beforehide : true,
1572         /**
1573          * @event show
1574          * Fires after this menu is displayed
1575          * @param {Roo.menu.Menu} this
1576          */
1577         show : true,
1578         /**
1579          * @event hide
1580          * Fires after this menu is hidden
1581          * @param {Roo.menu.Menu} this
1582          */
1583         hide : true,
1584         /**
1585          * @event click
1586          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1587          * @param {Roo.menu.Menu} this
1588          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1589          * @param {Roo.EventObject} e
1590          */
1591         click : true,
1592         /**
1593          * @event mouseover
1594          * Fires when the mouse is hovering over this menu
1595          * @param {Roo.menu.Menu} this
1596          * @param {Roo.EventObject} e
1597          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1598          */
1599         mouseover : true,
1600         /**
1601          * @event mouseout
1602          * Fires when the mouse exits this menu
1603          * @param {Roo.menu.Menu} this
1604          * @param {Roo.EventObject} e
1605          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1606          */
1607         mouseout : true,
1608         /**
1609          * @event itemclick
1610          * Fires when a menu item contained in this menu is clicked
1611          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1612          * @param {Roo.EventObject} e
1613          */
1614         itemclick: true
1615     });
1616     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1617 };
1618
1619 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1620     
1621    /// html : false,
1622     //align : '',
1623     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1624     type: false,
1625     /**
1626      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1627      */
1628     registerMenu : true,
1629     
1630     menuItems :false, // stores the menu items..
1631     
1632     hidden:true,
1633     
1634     parentMenu : false,
1635     
1636     getChildContainer : function() {
1637         return this.el;  
1638     },
1639     
1640     getAutoCreate : function(){
1641          
1642         //if (['right'].indexOf(this.align)!==-1) {
1643         //    cfg.cn[1].cls += ' pull-right'
1644         //}
1645         
1646         
1647         var cfg = {
1648             tag : 'ul',
1649             cls : 'dropdown-menu' ,
1650             style : 'z-index:1000'
1651             
1652         }
1653         
1654         if (this.type === 'submenu') {
1655             cfg.cls = 'submenu active';
1656         }
1657         if (this.type === 'treeview') {
1658             cfg.cls = 'treeview-menu';
1659         }
1660         
1661         return cfg;
1662     },
1663     initEvents : function() {
1664         
1665        // Roo.log("ADD event");
1666        // Roo.log(this.triggerEl.dom);
1667         this.triggerEl.on('click', this.onTriggerPress, this);
1668         this.triggerEl.addClass('dropdown-toggle');
1669         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1670
1671         this.el.on("mouseover", this.onMouseOver, this);
1672         this.el.on("mouseout", this.onMouseOut, this);
1673         
1674         
1675     },
1676     findTargetItem : function(e){
1677         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1678         if(!t){
1679             return false;
1680         }
1681         //Roo.log(t);         Roo.log(t.id);
1682         if(t && t.id){
1683             //Roo.log(this.menuitems);
1684             return this.menuitems.get(t.id);
1685             
1686             //return this.items.get(t.menuItemId);
1687         }
1688         
1689         return false;
1690     },
1691     onClick : function(e){
1692         Roo.log("menu.onClick");
1693         var t = this.findTargetItem(e);
1694         if(!t || t.isContainer){
1695             return;
1696         }
1697         Roo.log(e);
1698         /*
1699         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1700             if(t == this.activeItem && t.shouldDeactivate(e)){
1701                 this.activeItem.deactivate();
1702                 delete this.activeItem;
1703                 return;
1704             }
1705             if(t.canActivate){
1706                 this.setActiveItem(t, true);
1707             }
1708             return;
1709             
1710             
1711         }
1712         */
1713        
1714         Roo.log('pass click event');
1715         
1716         t.onClick(e);
1717         
1718         this.fireEvent("click", this, t, e);
1719         
1720         this.hide();
1721     },
1722      onMouseOver : function(e){
1723         var t  = this.findTargetItem(e);
1724         //Roo.log(t);
1725         //if(t){
1726         //    if(t.canActivate && !t.disabled){
1727         //        this.setActiveItem(t, true);
1728         //    }
1729         //}
1730         
1731         this.fireEvent("mouseover", this, e, t);
1732     },
1733     isVisible : function(){
1734         return !this.hidden;
1735     },
1736      onMouseOut : function(e){
1737         var t  = this.findTargetItem(e);
1738         
1739         //if(t ){
1740         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1741         //        this.activeItem.deactivate();
1742         //        delete this.activeItem;
1743         //    }
1744         //}
1745         this.fireEvent("mouseout", this, e, t);
1746     },
1747     
1748     
1749     /**
1750      * Displays this menu relative to another element
1751      * @param {String/HTMLElement/Roo.Element} element The element to align to
1752      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1753      * the element (defaults to this.defaultAlign)
1754      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1755      */
1756     show : function(el, pos, parentMenu){
1757         this.parentMenu = parentMenu;
1758         if(!this.el){
1759             this.render();
1760         }
1761         this.fireEvent("beforeshow", this);
1762         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1763     },
1764      /**
1765      * Displays this menu at a specific xy position
1766      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1767      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1768      */
1769     showAt : function(xy, parentMenu, /* private: */_e){
1770         this.parentMenu = parentMenu;
1771         if(!this.el){
1772             this.render();
1773         }
1774         if(_e !== false){
1775             this.fireEvent("beforeshow", this);
1776             
1777             //xy = this.el.adjustForConstraints(xy);
1778         }
1779         //this.el.setXY(xy);
1780         //this.el.show();
1781         this.hideMenuItems();
1782         this.hidden = false;
1783         this.triggerEl.addClass('open');
1784         this.focus();
1785         this.fireEvent("show", this);
1786     },
1787     
1788     focus : function(){
1789         return;
1790         if(!this.hidden){
1791             this.doFocus.defer(50, this);
1792         }
1793     },
1794
1795     doFocus : function(){
1796         if(!this.hidden){
1797             this.focusEl.focus();
1798         }
1799     },
1800
1801     /**
1802      * Hides this menu and optionally all parent menus
1803      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1804      */
1805     hide : function(deep){
1806         
1807         this.hideMenuItems();
1808         if(this.el && this.isVisible()){
1809             this.fireEvent("beforehide", this);
1810             if(this.activeItem){
1811                 this.activeItem.deactivate();
1812                 this.activeItem = null;
1813             }
1814             this.triggerEl.removeClass('open');;
1815             this.hidden = true;
1816             this.fireEvent("hide", this);
1817         }
1818         if(deep === true && this.parentMenu){
1819             this.parentMenu.hide(true);
1820         }
1821     },
1822     
1823     onTriggerPress  : function(e)
1824     {
1825         
1826         Roo.log('trigger press');
1827         //Roo.log(e.getTarget());
1828        // Roo.log(this.triggerEl.dom);
1829         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1830             return;
1831         }
1832         if (this.isVisible()) {
1833             Roo.log('hide');
1834             this.hide();
1835         } else {
1836             this.show(this.triggerEl, false, false);
1837         }
1838         
1839         
1840     },
1841     
1842          
1843        
1844     
1845     hideMenuItems : function()
1846     {
1847         //$(backdrop).remove()
1848         Roo.select('.open',true).each(function(aa) {
1849             
1850             aa.removeClass('open');
1851           //var parent = getParent($(this))
1852           //var relatedTarget = { relatedTarget: this }
1853           
1854            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1855           //if (e.isDefaultPrevented()) return
1856            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1857         })
1858     },
1859     addxtypeChild : function (tree, cntr) {
1860         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1861           
1862         this.menuitems.add(comp);
1863         return comp;
1864
1865     },
1866     getEl : function()
1867     {
1868         Roo.log(this.el);
1869         return this.el;
1870     }
1871 });
1872
1873  
1874  /*
1875  * - LGPL
1876  *
1877  * menu item
1878  * 
1879  */
1880
1881
1882 /**
1883  * @class Roo.bootstrap.MenuItem
1884  * @extends Roo.bootstrap.Component
1885  * Bootstrap MenuItem class
1886  * @cfg {String} html the menu label
1887  * @cfg {String} href the link
1888  * @cfg {Boolean} preventDefault (true | false) default true
1889  * @cfg {Boolean} isContainer (true | false) default false
1890  * 
1891  * 
1892  * @constructor
1893  * Create a new MenuItem
1894  * @param {Object} config The config object
1895  */
1896
1897
1898 Roo.bootstrap.MenuItem = function(config){
1899     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1900     this.addEvents({
1901         // raw events
1902         /**
1903          * @event click
1904          * The raw click event for the entire grid.
1905          * @param {Roo.EventObject} e
1906          */
1907         "click" : true
1908     });
1909 };
1910
1911 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1912     
1913     href : false,
1914     html : false,
1915     preventDefault: true,
1916     isContainer : false,
1917     
1918     getAutoCreate : function(){
1919         
1920         if(this.isContainer){
1921             return {
1922                 tag: 'li',
1923                 cls: 'dropdown-menu-item'
1924             };
1925         }
1926         
1927         var cfg= {
1928             tag: 'li',
1929             cls: 'dropdown-menu-item',
1930             cn: [
1931                     {
1932                         tag : 'a',
1933                         href : '#',
1934                         html : 'Link'
1935                     }
1936                 ]
1937         };
1938         if (this.parent().type == 'treeview') {
1939             cfg.cls = 'treeview-menu';
1940         }
1941         
1942         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1943         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1944         return cfg;
1945     },
1946     
1947     initEvents: function() {
1948         
1949         //this.el.select('a').on('click', this.onClick, this);
1950         
1951     },
1952     onClick : function(e)
1953     {
1954         Roo.log('item on click ');
1955         //if(this.preventDefault){
1956         //    e.preventDefault();
1957         //}
1958         //this.parent().hideMenuItems();
1959         
1960         this.fireEvent('click', this, e);
1961     },
1962     getEl : function()
1963     {
1964         return this.el;
1965     }
1966 });
1967
1968  
1969
1970  /*
1971  * - LGPL
1972  *
1973  * menu separator
1974  * 
1975  */
1976
1977
1978 /**
1979  * @class Roo.bootstrap.MenuSeparator
1980  * @extends Roo.bootstrap.Component
1981  * Bootstrap MenuSeparator class
1982  * 
1983  * @constructor
1984  * Create a new MenuItem
1985  * @param {Object} config The config object
1986  */
1987
1988
1989 Roo.bootstrap.MenuSeparator = function(config){
1990     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1991 };
1992
1993 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1994     
1995     getAutoCreate : function(){
1996         var cfg = {
1997             cls: 'divider',
1998             tag : 'li'
1999         };
2000         
2001         return cfg;
2002     }
2003    
2004 });
2005
2006  
2007
2008  
2009 /*
2010 <div class="modal fade">
2011   <div class="modal-dialog">
2012     <div class="modal-content">
2013       <div class="modal-header">
2014         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2015         <h4 class="modal-title">Modal title</h4>
2016       </div>
2017       <div class="modal-body">
2018         <p>One fine body&hellip;</p>
2019       </div>
2020       <div class="modal-footer">
2021         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2022         <button type="button" class="btn btn-primary">Save changes</button>
2023       </div>
2024     </div><!-- /.modal-content -->
2025   </div><!-- /.modal-dialog -->
2026 </div><!-- /.modal -->
2027 */
2028 /*
2029  * - LGPL
2030  *
2031  * page contgainer.
2032  * 
2033  */
2034
2035 /**
2036  * @class Roo.bootstrap.Modal
2037  * @extends Roo.bootstrap.Component
2038  * Bootstrap Modal class
2039  * @cfg {String} title Title of dialog
2040  * @cfg {Boolean} specificTitle (true|false) default false
2041  * @cfg {Array} buttons Array of buttons or standard button set..
2042  * @cfg {String} buttonPosition (left|right|center) default right
2043  * @cfg {Boolean} animate (true | false) default true
2044  * 
2045  * @constructor
2046  * Create a new Modal Dialog
2047  * @param {Object} config The config object
2048  */
2049
2050 Roo.bootstrap.Modal = function(config){
2051     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2052     this.addEvents({
2053         // raw events
2054         /**
2055          * @event btnclick
2056          * The raw btnclick event for the button
2057          * @param {Roo.EventObject} e
2058          */
2059         "btnclick" : true
2060     });
2061     this.buttons = this.buttons || [];
2062 };
2063
2064 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2065     
2066     title : 'test dialog',
2067    
2068     buttons : false,
2069     
2070     // set on load...
2071     body:  false,
2072     
2073     specificTitle: false,
2074     
2075     buttonPosition: 'right',
2076     
2077     animate : true,
2078     
2079     onRender : function(ct, position)
2080     {
2081         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2082      
2083         if(!this.el){
2084             var cfg = Roo.apply({},  this.getAutoCreate());
2085             cfg.id = Roo.id();
2086             //if(!cfg.name){
2087             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2088             //}
2089             //if (!cfg.name.length) {
2090             //    delete cfg.name;
2091            // }
2092             if (this.cls) {
2093                 cfg.cls += ' ' + this.cls;
2094             }
2095             if (this.style) {
2096                 cfg.style = this.style;
2097             }
2098             this.el = Roo.get(document.body).createChild(cfg, position);
2099         }
2100         //var type = this.el.dom.type;
2101         
2102         if(this.tabIndex !== undefined){
2103             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2104         }
2105         
2106         
2107         
2108         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2109         this.maskEl.enableDisplayMode("block");
2110         this.maskEl.hide();
2111         //this.el.addClass("x-dlg-modal");
2112     
2113         if (this.buttons.length) {
2114             Roo.each(this.buttons, function(bb) {
2115                 b = Roo.apply({}, bb);
2116                 b.xns = b.xns || Roo.bootstrap;
2117                 b.xtype = b.xtype || 'Button';
2118                 if (typeof(b.listeners) == 'undefined') {
2119                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2120                 }
2121                 
2122                 var btn = Roo.factory(b);
2123                 
2124                 btn.onRender(this.el.select('.modal-footer div').first());
2125                 
2126             },this);
2127         }
2128         // render the children.
2129         var nitems = [];
2130         
2131         if(typeof(this.items) != 'undefined'){
2132             var items = this.items;
2133             delete this.items;
2134
2135             for(var i =0;i < items.length;i++) {
2136                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2137             }
2138         }
2139         
2140         this.items = nitems;
2141         
2142         this.body = this.el.select('.modal-body',true).first();
2143         this.close = this.el.select('.modal-header .close', true).first();
2144         this.footer = this.el.select('.modal-footer',true).first();
2145         this.initEvents();
2146         //this.el.addClass([this.fieldClass, this.cls]);
2147         
2148     },
2149     getAutoCreate : function(){
2150         
2151         
2152         var bdy = {
2153                 cls : 'modal-body',
2154                 html : this.html || ''
2155         };
2156         
2157         var title = {
2158             tag: 'h4',
2159             cls : 'modal-title',
2160             html : this.title
2161         };
2162         
2163         if(this.specificTitle){
2164             title = this.title;
2165         };
2166         
2167         var modal = {
2168             cls: "modal",
2169             style : 'display: none',
2170             cn : [
2171                 {
2172                     cls: "modal-dialog",
2173                     cn : [
2174                         {
2175                             cls : "modal-content",
2176                             cn : [
2177                                 {
2178                                     cls : 'modal-header',
2179                                     cn : [
2180                                         {
2181                                             tag: 'button',
2182                                             cls : 'close',
2183                                             html : '&times'
2184                                         },
2185                                         title
2186                                     ]
2187                                 },
2188                                 bdy,
2189                                 {
2190                                     cls : 'modal-footer',
2191                                     cn : [
2192                                         {
2193                                             tag: 'div',
2194                                             cls: 'btn-' + this.buttonPosition
2195                                         }
2196                                     ]
2197                                     
2198                                 }
2199                                 
2200                                 
2201                             ]
2202                             
2203                         }
2204                     ]
2205                         
2206                 }
2207             ]
2208         };
2209         
2210         if(this.animate){
2211             modal.cls += ' fade';
2212         }
2213         
2214         return modal;
2215           
2216     },
2217     getChildContainer : function() {
2218          
2219          return this.el.select('.modal-body',true).first();
2220         
2221     },
2222     getButtonContainer : function() {
2223          return this.el.select('.modal-footer div',true).first();
2224         
2225     },
2226     initEvents : function()
2227     {
2228         this.el.select('.modal-header .close').on('click', this.hide, this);
2229 //        
2230 //        this.addxtype(this);
2231     },
2232     show : function() {
2233         
2234         if (!this.rendered) {
2235             this.render();
2236         }
2237         
2238         this.el.setStyle('display', 'block');
2239         
2240         if(this.animate){
2241             var _this = this;
2242             (function(){ _this.el.addClass('in'); }).defer(50);
2243         }else{
2244             this.el.addClass('in');
2245         }
2246         
2247         Roo.get(document.body).addClass("x-body-masked");
2248         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2249         this.maskEl.show();
2250         this.el.setStyle('zIndex', '10001');
2251         this.fireEvent('show', this);
2252         
2253         
2254     },
2255     hide : function()
2256     {
2257         this.maskEl.hide();
2258         Roo.get(document.body).removeClass("x-body-masked");
2259         this.el.removeClass('in');
2260         
2261         if(this.animate){
2262             var _this = this;
2263             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2264         }else{
2265             this.el.setStyle('display', 'none');
2266         }
2267         
2268         this.fireEvent('hide', this);
2269     },
2270     
2271     addButton : function(str, cb)
2272     {
2273          
2274         
2275         var b = Roo.apply({}, { html : str } );
2276         b.xns = b.xns || Roo.bootstrap;
2277         b.xtype = b.xtype || 'Button';
2278         if (typeof(b.listeners) == 'undefined') {
2279             b.listeners = { click : cb.createDelegate(this)  };
2280         }
2281         
2282         var btn = Roo.factory(b);
2283            
2284         btn.onRender(this.el.select('.modal-footer div').first());
2285         
2286         return btn;   
2287        
2288     },
2289     
2290     setDefaultButton : function(btn)
2291     {
2292         //this.el.select('.modal-footer').()
2293     },
2294     resizeTo: function(w,h)
2295     {
2296         // skip..
2297     },
2298     setContentSize  : function(w, h)
2299     {
2300         
2301     },
2302     onButtonClick: function(btn,e)
2303     {
2304         //Roo.log([a,b,c]);
2305         this.fireEvent('btnclick', btn.name, e);
2306     },
2307     setTitle: function(str) {
2308         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2309         
2310     }
2311 });
2312
2313
2314 Roo.apply(Roo.bootstrap.Modal,  {
2315     /**
2316          * Button config that displays a single OK button
2317          * @type Object
2318          */
2319         OK :  [{
2320             name : 'ok',
2321             weight : 'primary',
2322             html : 'OK'
2323         }], 
2324         /**
2325          * Button config that displays Yes and No buttons
2326          * @type Object
2327          */
2328         YESNO : [
2329             {
2330                 name  : 'no',
2331                 html : 'No'
2332             },
2333             {
2334                 name  :'yes',
2335                 weight : 'primary',
2336                 html : 'Yes'
2337             }
2338         ],
2339         
2340         /**
2341          * Button config that displays OK and Cancel buttons
2342          * @type Object
2343          */
2344         OKCANCEL : [
2345             {
2346                name : 'cancel',
2347                 html : 'Cancel'
2348             },
2349             {
2350                 name : 'ok',
2351                 weight : 'primary',
2352                 html : 'OK'
2353             }
2354         ],
2355         /**
2356          * Button config that displays Yes, No and Cancel buttons
2357          * @type Object
2358          */
2359         YESNOCANCEL : [
2360             {
2361                 name : 'yes',
2362                 weight : 'primary',
2363                 html : 'Yes'
2364             },
2365             {
2366                 name : 'no',
2367                 html : 'No'
2368             },
2369             {
2370                 name : 'cancel',
2371                 html : 'Cancel'
2372             }
2373         ]
2374 });
2375  /*
2376  * - LGPL
2377  *
2378  * messagebox - can be used as a replace
2379  * 
2380  */
2381 /**
2382  * @class Roo.MessageBox
2383  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2384  * Example usage:
2385  *<pre><code>
2386 // Basic alert:
2387 Roo.Msg.alert('Status', 'Changes saved successfully.');
2388
2389 // Prompt for user data:
2390 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2391     if (btn == 'ok'){
2392         // process text value...
2393     }
2394 });
2395
2396 // Show a dialog using config options:
2397 Roo.Msg.show({
2398    title:'Save Changes?',
2399    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2400    buttons: Roo.Msg.YESNOCANCEL,
2401    fn: processResult,
2402    animEl: 'elId'
2403 });
2404 </code></pre>
2405  * @singleton
2406  */
2407 Roo.bootstrap.MessageBox = function(){
2408     var dlg, opt, mask, waitTimer;
2409     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2410     var buttons, activeTextEl, bwidth;
2411
2412     
2413     // private
2414     var handleButton = function(button){
2415         dlg.hide();
2416         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2417     };
2418
2419     // private
2420     var handleHide = function(){
2421         if(opt && opt.cls){
2422             dlg.el.removeClass(opt.cls);
2423         }
2424         //if(waitTimer){
2425         //    Roo.TaskMgr.stop(waitTimer);
2426         //    waitTimer = null;
2427         //}
2428     };
2429
2430     // private
2431     var updateButtons = function(b){
2432         var width = 0;
2433         if(!b){
2434             buttons["ok"].hide();
2435             buttons["cancel"].hide();
2436             buttons["yes"].hide();
2437             buttons["no"].hide();
2438             //dlg.footer.dom.style.display = 'none';
2439             return width;
2440         }
2441         dlg.footer.dom.style.display = '';
2442         for(var k in buttons){
2443             if(typeof buttons[k] != "function"){
2444                 if(b[k]){
2445                     buttons[k].show();
2446                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2447                     width += buttons[k].el.getWidth()+15;
2448                 }else{
2449                     buttons[k].hide();
2450                 }
2451             }
2452         }
2453         return width;
2454     };
2455
2456     // private
2457     var handleEsc = function(d, k, e){
2458         if(opt && opt.closable !== false){
2459             dlg.hide();
2460         }
2461         if(e){
2462             e.stopEvent();
2463         }
2464     };
2465
2466     return {
2467         /**
2468          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2469          * @return {Roo.BasicDialog} The BasicDialog element
2470          */
2471         getDialog : function(){
2472            if(!dlg){
2473                 dlg = new Roo.bootstrap.Modal( {
2474                     //draggable: true,
2475                     //resizable:false,
2476                     //constraintoviewport:false,
2477                     //fixedcenter:true,
2478                     //collapsible : false,
2479                     //shim:true,
2480                     //modal: true,
2481                   //  width:400,
2482                   //  height:100,
2483                     //buttonAlign:"center",
2484                     closeClick : function(){
2485                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2486                             handleButton("no");
2487                         }else{
2488                             handleButton("cancel");
2489                         }
2490                     }
2491                 });
2492                 dlg.render();
2493                 dlg.on("hide", handleHide);
2494                 mask = dlg.mask;
2495                 //dlg.addKeyListener(27, handleEsc);
2496                 buttons = {};
2497                 this.buttons = buttons;
2498                 var bt = this.buttonText;
2499                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2500                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2501                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2502                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2503                 Roo.log(buttons)
2504                 bodyEl = dlg.body.createChild({
2505
2506                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2507                         '<textarea class="roo-mb-textarea"></textarea>' +
2508                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2509                 });
2510                 msgEl = bodyEl.dom.firstChild;
2511                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2512                 textboxEl.enableDisplayMode();
2513                 textboxEl.addKeyListener([10,13], function(){
2514                     if(dlg.isVisible() && opt && opt.buttons){
2515                         if(opt.buttons.ok){
2516                             handleButton("ok");
2517                         }else if(opt.buttons.yes){
2518                             handleButton("yes");
2519                         }
2520                     }
2521                 });
2522                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2523                 textareaEl.enableDisplayMode();
2524                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2525                 progressEl.enableDisplayMode();
2526                 var pf = progressEl.dom.firstChild;
2527                 if (pf) {
2528                     pp = Roo.get(pf.firstChild);
2529                     pp.setHeight(pf.offsetHeight);
2530                 }
2531                 
2532             }
2533             return dlg;
2534         },
2535
2536         /**
2537          * Updates the message box body text
2538          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2539          * the XHTML-compliant non-breaking space character '&amp;#160;')
2540          * @return {Roo.MessageBox} This message box
2541          */
2542         updateText : function(text){
2543             if(!dlg.isVisible() && !opt.width){
2544                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2545             }
2546             msgEl.innerHTML = text || '&#160;';
2547       
2548             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2549             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2550             var w = Math.max(
2551                     Math.min(opt.width || cw , this.maxWidth), 
2552                     Math.max(opt.minWidth || this.minWidth, bwidth)
2553             );
2554             if(opt.prompt){
2555                 activeTextEl.setWidth(w);
2556             }
2557             if(dlg.isVisible()){
2558                 dlg.fixedcenter = false;
2559             }
2560             // to big, make it scroll. = But as usual stupid IE does not support
2561             // !important..
2562             
2563             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2564                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2565                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2566             } else {
2567                 bodyEl.dom.style.height = '';
2568                 bodyEl.dom.style.overflowY = '';
2569             }
2570             if (cw > w) {
2571                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2572             } else {
2573                 bodyEl.dom.style.overflowX = '';
2574             }
2575             
2576             dlg.setContentSize(w, bodyEl.getHeight());
2577             if(dlg.isVisible()){
2578                 dlg.fixedcenter = true;
2579             }
2580             return this;
2581         },
2582
2583         /**
2584          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2585          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2586          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2587          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2588          * @return {Roo.MessageBox} This message box
2589          */
2590         updateProgress : function(value, text){
2591             if(text){
2592                 this.updateText(text);
2593             }
2594             if (pp) { // weird bug on my firefox - for some reason this is not defined
2595                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2596             }
2597             return this;
2598         },        
2599
2600         /**
2601          * Returns true if the message box is currently displayed
2602          * @return {Boolean} True if the message box is visible, else false
2603          */
2604         isVisible : function(){
2605             return dlg && dlg.isVisible();  
2606         },
2607
2608         /**
2609          * Hides the message box if it is displayed
2610          */
2611         hide : function(){
2612             if(this.isVisible()){
2613                 dlg.hide();
2614             }  
2615         },
2616
2617         /**
2618          * Displays a new message box, or reinitializes an existing message box, based on the config options
2619          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2620          * The following config object properties are supported:
2621          * <pre>
2622 Property    Type             Description
2623 ----------  ---------------  ------------------------------------------------------------------------------------
2624 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2625                                    closes (defaults to undefined)
2626 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2627                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2628 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2629                                    progress and wait dialogs will ignore this property and always hide the
2630                                    close button as they can only be closed programmatically.
2631 cls               String           A custom CSS class to apply to the message box element
2632 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2633                                    displayed (defaults to 75)
2634 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2635                                    function will be btn (the name of the button that was clicked, if applicable,
2636                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2637                                    Progress and wait dialogs will ignore this option since they do not respond to
2638                                    user actions and can only be closed programmatically, so any required function
2639                                    should be called by the same code after it closes the dialog.
2640 icon              String           A CSS class that provides a background image to be used as an icon for
2641                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2642 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2643 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2644 modal             Boolean          False to allow user interaction with the page while the message box is
2645                                    displayed (defaults to true)
2646 msg               String           A string that will replace the existing message box body text (defaults
2647                                    to the XHTML-compliant non-breaking space character '&#160;')
2648 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2649 progress          Boolean          True to display a progress bar (defaults to false)
2650 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2651 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2652 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2653 title             String           The title text
2654 value             String           The string value to set into the active textbox element if displayed
2655 wait              Boolean          True to display a progress bar (defaults to false)
2656 width             Number           The width of the dialog in pixels
2657 </pre>
2658          *
2659          * Example usage:
2660          * <pre><code>
2661 Roo.Msg.show({
2662    title: 'Address',
2663    msg: 'Please enter your address:',
2664    width: 300,
2665    buttons: Roo.MessageBox.OKCANCEL,
2666    multiline: true,
2667    fn: saveAddress,
2668    animEl: 'addAddressBtn'
2669 });
2670 </code></pre>
2671          * @param {Object} config Configuration options
2672          * @return {Roo.MessageBox} This message box
2673          */
2674         show : function(options)
2675         {
2676             
2677             // this causes nightmares if you show one dialog after another
2678             // especially on callbacks..
2679              
2680             if(this.isVisible()){
2681                 
2682                 this.hide();
2683                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2684                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2685                 Roo.log("New Dialog Message:" +  options.msg )
2686                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2687                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2688                 
2689             }
2690             var d = this.getDialog();
2691             opt = options;
2692             d.setTitle(opt.title || "&#160;");
2693             d.close.setDisplayed(opt.closable !== false);
2694             activeTextEl = textboxEl;
2695             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2696             if(opt.prompt){
2697                 if(opt.multiline){
2698                     textboxEl.hide();
2699                     textareaEl.show();
2700                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2701                         opt.multiline : this.defaultTextHeight);
2702                     activeTextEl = textareaEl;
2703                 }else{
2704                     textboxEl.show();
2705                     textareaEl.hide();
2706                 }
2707             }else{
2708                 textboxEl.hide();
2709                 textareaEl.hide();
2710             }
2711             progressEl.setDisplayed(opt.progress === true);
2712             this.updateProgress(0);
2713             activeTextEl.dom.value = opt.value || "";
2714             if(opt.prompt){
2715                 dlg.setDefaultButton(activeTextEl);
2716             }else{
2717                 var bs = opt.buttons;
2718                 var db = null;
2719                 if(bs && bs.ok){
2720                     db = buttons["ok"];
2721                 }else if(bs && bs.yes){
2722                     db = buttons["yes"];
2723                 }
2724                 dlg.setDefaultButton(db);
2725             }
2726             bwidth = updateButtons(opt.buttons);
2727             this.updateText(opt.msg);
2728             if(opt.cls){
2729                 d.el.addClass(opt.cls);
2730             }
2731             d.proxyDrag = opt.proxyDrag === true;
2732             d.modal = opt.modal !== false;
2733             d.mask = opt.modal !== false ? mask : false;
2734             if(!d.isVisible()){
2735                 // force it to the end of the z-index stack so it gets a cursor in FF
2736                 document.body.appendChild(dlg.el.dom);
2737                 d.animateTarget = null;
2738                 d.show(options.animEl);
2739             }
2740             return this;
2741         },
2742
2743         /**
2744          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2745          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2746          * and closing the message box when the process is complete.
2747          * @param {String} title The title bar text
2748          * @param {String} msg The message box body text
2749          * @return {Roo.MessageBox} This message box
2750          */
2751         progress : function(title, msg){
2752             this.show({
2753                 title : title,
2754                 msg : msg,
2755                 buttons: false,
2756                 progress:true,
2757                 closable:false,
2758                 minWidth: this.minProgressWidth,
2759                 modal : true
2760             });
2761             return this;
2762         },
2763
2764         /**
2765          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2766          * If a callback function is passed it will be called after the user clicks the button, and the
2767          * id of the button that was clicked will be passed as the only parameter to the callback
2768          * (could also be the top-right close button).
2769          * @param {String} title The title bar text
2770          * @param {String} msg The message box body text
2771          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2772          * @param {Object} scope (optional) The scope of the callback function
2773          * @return {Roo.MessageBox} This message box
2774          */
2775         alert : function(title, msg, fn, scope){
2776             this.show({
2777                 title : title,
2778                 msg : msg,
2779                 buttons: this.OK,
2780                 fn: fn,
2781                 scope : scope,
2782                 modal : true
2783             });
2784             return this;
2785         },
2786
2787         /**
2788          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2789          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2790          * You are responsible for closing the message box when the process is complete.
2791          * @param {String} msg The message box body text
2792          * @param {String} title (optional) The title bar text
2793          * @return {Roo.MessageBox} This message box
2794          */
2795         wait : function(msg, title){
2796             this.show({
2797                 title : title,
2798                 msg : msg,
2799                 buttons: false,
2800                 closable:false,
2801                 progress:true,
2802                 modal:true,
2803                 width:300,
2804                 wait:true
2805             });
2806             waitTimer = Roo.TaskMgr.start({
2807                 run: function(i){
2808                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2809                 },
2810                 interval: 1000
2811             });
2812             return this;
2813         },
2814
2815         /**
2816          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2817          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2818          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2819          * @param {String} title The title bar text
2820          * @param {String} msg The message box body text
2821          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2822          * @param {Object} scope (optional) The scope of the callback function
2823          * @return {Roo.MessageBox} This message box
2824          */
2825         confirm : function(title, msg, fn, scope){
2826             this.show({
2827                 title : title,
2828                 msg : msg,
2829                 buttons: this.YESNO,
2830                 fn: fn,
2831                 scope : scope,
2832                 modal : true
2833             });
2834             return this;
2835         },
2836
2837         /**
2838          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2839          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2840          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2841          * (could also be the top-right close button) and the text that was entered will be passed as the two
2842          * parameters to the callback.
2843          * @param {String} title The title bar text
2844          * @param {String} msg The message box body text
2845          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2846          * @param {Object} scope (optional) The scope of the callback function
2847          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2848          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2849          * @return {Roo.MessageBox} This message box
2850          */
2851         prompt : function(title, msg, fn, scope, multiline){
2852             this.show({
2853                 title : title,
2854                 msg : msg,
2855                 buttons: this.OKCANCEL,
2856                 fn: fn,
2857                 minWidth:250,
2858                 scope : scope,
2859                 prompt:true,
2860                 multiline: multiline,
2861                 modal : true
2862             });
2863             return this;
2864         },
2865
2866         /**
2867          * Button config that displays a single OK button
2868          * @type Object
2869          */
2870         OK : {ok:true},
2871         /**
2872          * Button config that displays Yes and No buttons
2873          * @type Object
2874          */
2875         YESNO : {yes:true, no:true},
2876         /**
2877          * Button config that displays OK and Cancel buttons
2878          * @type Object
2879          */
2880         OKCANCEL : {ok:true, cancel:true},
2881         /**
2882          * Button config that displays Yes, No and Cancel buttons
2883          * @type Object
2884          */
2885         YESNOCANCEL : {yes:true, no:true, cancel:true},
2886
2887         /**
2888          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2889          * @type Number
2890          */
2891         defaultTextHeight : 75,
2892         /**
2893          * The maximum width in pixels of the message box (defaults to 600)
2894          * @type Number
2895          */
2896         maxWidth : 600,
2897         /**
2898          * The minimum width in pixels of the message box (defaults to 100)
2899          * @type Number
2900          */
2901         minWidth : 100,
2902         /**
2903          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2904          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2905          * @type Number
2906          */
2907         minProgressWidth : 250,
2908         /**
2909          * An object containing the default button text strings that can be overriden for localized language support.
2910          * Supported properties are: ok, cancel, yes and no.
2911          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2912          * @type Object
2913          */
2914         buttonText : {
2915             ok : "OK",
2916             cancel : "Cancel",
2917             yes : "Yes",
2918             no : "No"
2919         }
2920     };
2921 }();
2922
2923 /**
2924  * Shorthand for {@link Roo.MessageBox}
2925  */
2926 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2927 Roo.Msg = Roo.Msg || Roo.MessageBox;
2928 /*
2929  * - LGPL
2930  *
2931  * navbar
2932  * 
2933  */
2934
2935 /**
2936  * @class Roo.bootstrap.Navbar
2937  * @extends Roo.bootstrap.Component
2938  * Bootstrap Navbar class
2939
2940  * @constructor
2941  * Create a new Navbar
2942  * @param {Object} config The config object
2943  */
2944
2945
2946 Roo.bootstrap.Navbar = function(config){
2947     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2948     
2949 };
2950
2951 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2952     
2953     
2954    
2955     // private
2956     navItems : false,
2957     loadMask : false,
2958     
2959     
2960     getAutoCreate : function(){
2961         
2962         
2963         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2964         
2965     },
2966     
2967     initEvents :function ()
2968     {
2969         //Roo.log(this.el.select('.navbar-toggle',true));
2970         this.el.select('.navbar-toggle',true).on('click', function() {
2971            // Roo.log('click');
2972             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2973         }, this);
2974         
2975         var mark = {
2976             tag: "div",
2977             cls:"x-dlg-mask"
2978         }
2979         
2980         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2981         
2982         var size = this.el.getSize();
2983         this.maskEl.setSize(size.width, size.height);
2984         this.maskEl.enableDisplayMode("block");
2985         this.maskEl.hide();
2986         
2987         if(this.loadMask){
2988             this.maskEl.show();
2989         }
2990     },
2991     
2992     
2993     getChildContainer : function()
2994     {
2995         if (this.el.select('.collapse').getCount()) {
2996             return this.el.select('.collapse',true).first();
2997         }
2998         
2999         return this.el;
3000     },
3001     
3002     mask : function()
3003     {
3004         this.maskEl.show();
3005     },
3006     
3007     unmask : function()
3008     {
3009         this.maskEl.hide();
3010     } 
3011     
3012     
3013     
3014     
3015 });
3016
3017
3018
3019  
3020
3021  /*
3022  * - LGPL
3023  *
3024  * navbar
3025  * 
3026  */
3027
3028 /**
3029  * @class Roo.bootstrap.NavSimplebar
3030  * @extends Roo.bootstrap.Navbar
3031  * Bootstrap Sidebar class
3032  *
3033  * @cfg {Boolean} inverse is inverted color
3034  * 
3035  * @cfg {String} type (nav | pills | tabs)
3036  * @cfg {Boolean} arrangement stacked | justified
3037  * @cfg {String} align (left | right) alignment
3038  * 
3039  * @cfg {Boolean} main (true|false) main nav bar? default false
3040  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3041  * 
3042  * @cfg {String} tag (header|footer|nav|div) default is nav 
3043
3044  * 
3045  * 
3046  * 
3047  * @constructor
3048  * Create a new Sidebar
3049  * @param {Object} config The config object
3050  */
3051
3052
3053 Roo.bootstrap.NavSimplebar = function(config){
3054     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3055 };
3056
3057 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3058     
3059     inverse: false,
3060     
3061     type: false,
3062     arrangement: '',
3063     align : false,
3064     
3065     
3066     
3067     main : false,
3068     
3069     
3070     tag : false,
3071     
3072     
3073     getAutoCreate : function(){
3074         
3075         
3076         var cfg = {
3077             tag : this.tag || 'div',
3078             cls : 'navbar'
3079         };
3080           
3081         
3082         cfg.cn = [
3083             {
3084                 cls: 'nav',
3085                 tag : 'ul'
3086             }
3087         ];
3088         
3089          
3090         this.type = this.type || 'nav';
3091         if (['tabs','pills'].indexOf(this.type)!==-1) {
3092             cfg.cn[0].cls += ' nav-' + this.type
3093         
3094         
3095         } else {
3096             if (this.type!=='nav') {
3097                 Roo.log('nav type must be nav/tabs/pills')
3098             }
3099             cfg.cn[0].cls += ' navbar-nav'
3100         }
3101         
3102         
3103         
3104         
3105         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3106             cfg.cn[0].cls += ' nav-' + this.arrangement;
3107         }
3108         
3109         
3110         if (this.align === 'right') {
3111             cfg.cn[0].cls += ' navbar-right';
3112         }
3113         
3114         if (this.inverse) {
3115             cfg.cls += ' navbar-inverse';
3116             
3117         }
3118         
3119         
3120         return cfg;
3121     
3122         
3123     }
3124     
3125     
3126     
3127 });
3128
3129
3130
3131  
3132
3133  
3134        /*
3135  * - LGPL
3136  *
3137  * navbar
3138  * 
3139  */
3140
3141 /**
3142  * @class Roo.bootstrap.NavHeaderbar
3143  * @extends Roo.bootstrap.NavSimplebar
3144  * Bootstrap Sidebar class
3145  *
3146  * @cfg {String} brand what is brand
3147  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3148  * @cfg {String} brand_href href of the brand
3149  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3150  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3151  * 
3152  * @constructor
3153  * Create a new Sidebar
3154  * @param {Object} config The config object
3155  */
3156
3157
3158 Roo.bootstrap.NavHeaderbar = function(config){
3159     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3160 };
3161
3162 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3163     
3164     position: '',
3165     brand: '',
3166     brand_href: false,
3167     srButton : true,
3168     autohide : false,
3169     
3170     getAutoCreate : function(){
3171         
3172         var   cfg = {
3173             tag: this.nav || 'nav',
3174             cls: 'navbar',
3175             role: 'navigation',
3176             cn: []
3177         };
3178         
3179         if(this.srButton){
3180             cfg.cn.push({
3181                 tag: 'div',
3182                 cls: 'navbar-header',
3183                 cn: [
3184                     {
3185                         tag: 'button',
3186                         type: 'button',
3187                         cls: 'navbar-toggle',
3188                         'data-toggle': 'collapse',
3189                         cn: [
3190                             {
3191                                 tag: 'span',
3192                                 cls: 'sr-only',
3193                                 html: 'Toggle navigation'
3194                             },
3195                             {
3196                                 tag: 'span',
3197                                 cls: 'icon-bar'
3198                             },
3199                             {
3200                                 tag: 'span',
3201                                 cls: 'icon-bar'
3202                             },
3203                             {
3204                                 tag: 'span',
3205                                 cls: 'icon-bar'
3206                             }
3207                         ]
3208                     }
3209                 ]
3210             });
3211         }
3212         
3213         cfg.cn.push({
3214             tag: 'div',
3215             cls: 'collapse navbar-collapse',
3216             cn : []
3217         });
3218         
3219         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3220         
3221         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3222             cfg.cls += ' navbar-' + this.position;
3223             
3224             // tag can override this..
3225             
3226             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3227         }
3228         
3229         if (this.brand !== '') {
3230             cfg.cn[0].cn.push({
3231                 tag: 'a',
3232                 href: this.brand_href ? this.brand_href : '#',
3233                 cls: 'navbar-brand',
3234                 cn: [
3235                 this.brand
3236                 ]
3237             });
3238         }
3239         
3240         if(this.main){
3241             cfg.cls += ' main-nav';
3242         }
3243         
3244         
3245         return cfg;
3246
3247         
3248     },
3249     initEvents : function()
3250     {
3251         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3252         
3253         if (this.autohide) {
3254             
3255             var prevScroll = 0;
3256             var ft = this.el;
3257             
3258             Roo.get(document).on('scroll',function(e) {
3259                 var ns = Roo.get(document).getScroll().top;
3260                 var os = prevScroll;
3261                 prevScroll = ns;
3262                 
3263                 if(ns > os){
3264                     ft.removeClass('slideDown');
3265                     ft.addClass('slideUp');
3266                     return;
3267                 }
3268                 ft.removeClass('slideUp');
3269                 ft.addClass('slideDown');
3270                  
3271               
3272           },this);
3273         }
3274     }    
3275           
3276       
3277     
3278     
3279 });
3280
3281
3282
3283  
3284
3285  /*
3286  * - LGPL
3287  *
3288  * navbar
3289  * 
3290  */
3291
3292 /**
3293  * @class Roo.bootstrap.NavSidebar
3294  * @extends Roo.bootstrap.Navbar
3295  * Bootstrap Sidebar class
3296  * 
3297  * @constructor
3298  * Create a new Sidebar
3299  * @param {Object} config The config object
3300  */
3301
3302
3303 Roo.bootstrap.NavSidebar = function(config){
3304     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3305 };
3306
3307 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3308     
3309     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3310     
3311     getAutoCreate : function(){
3312         
3313         
3314         return  {
3315             tag: 'div',
3316             cls: 'sidebar sidebar-nav'
3317         };
3318     
3319         
3320     }
3321     
3322     
3323     
3324 });
3325
3326
3327
3328  
3329
3330  /*
3331  * - LGPL
3332  *
3333  * nav group
3334  * 
3335  */
3336
3337 /**
3338  * @class Roo.bootstrap.NavGroup
3339  * @extends Roo.bootstrap.Component
3340  * Bootstrap NavGroup class
3341  * @cfg {String} align left | right
3342  * @cfg {Boolean} inverse false | true
3343  * @cfg {String} type (nav|pills|tab) default nav
3344  * @cfg {String} navId - reference Id for navbar.
3345
3346  * 
3347  * @constructor
3348  * Create a new nav group
3349  * @param {Object} config The config object
3350  */
3351
3352 Roo.bootstrap.NavGroup = function(config){
3353     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3354     this.navItems = [];
3355    
3356     Roo.bootstrap.NavGroup.register(this);
3357      this.addEvents({
3358         /**
3359              * @event changed
3360              * Fires when the active item changes
3361              * @param {Roo.bootstrap.NavGroup} this
3362              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3363              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3364          */
3365         'changed': true
3366      });
3367     
3368 };
3369
3370 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3371     
3372     align: '',
3373     inverse: false,
3374     form: false,
3375     type: 'nav',
3376     navId : '',
3377     // private
3378     
3379     navItems : false, 
3380     
3381     getAutoCreate : function()
3382     {
3383         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3384         
3385         cfg = {
3386             tag : 'ul',
3387             cls: 'nav' 
3388         }
3389         
3390         if (['tabs','pills'].indexOf(this.type)!==-1) {
3391             cfg.cls += ' nav-' + this.type
3392         } else {
3393             if (this.type!=='nav') {
3394                 Roo.log('nav type must be nav/tabs/pills')
3395             }
3396             cfg.cls += ' navbar-nav'
3397         }
3398         
3399         if (this.parent().sidebar) {
3400             cfg = {
3401                 tag: 'ul',
3402                 cls: 'dashboard-menu sidebar-menu'
3403             }
3404             
3405             return cfg;
3406         }
3407         
3408         if (this.form === true) {
3409             cfg = {
3410                 tag: 'form',
3411                 cls: 'navbar-form'
3412             }
3413             
3414             if (this.align === 'right') {
3415                 cfg.cls += ' navbar-right';
3416             } else {
3417                 cfg.cls += ' navbar-left';
3418             }
3419         }
3420         
3421         if (this.align === 'right') {
3422             cfg.cls += ' navbar-right';
3423         }
3424         
3425         if (this.inverse) {
3426             cfg.cls += ' navbar-inverse';
3427             
3428         }
3429         
3430         
3431         return cfg;
3432     },
3433     /**
3434     * sets the active Navigation item
3435     * @param {Roo.bootstrap.NavItem} the new current navitem
3436     */
3437     setActiveItem : function(item)
3438     {
3439         var prev = false;
3440         Roo.each(this.navItems, function(v){
3441             if (v == item) {
3442                 return ;
3443             }
3444             if (v.isActive()) {
3445                 v.setActive(false, true);
3446                 prev = v;
3447                 
3448             }
3449             
3450         });
3451
3452         item.setActive(true, true);
3453         this.fireEvent('changed', this, item, prev);
3454         
3455         
3456     },
3457     /**
3458     * gets the active Navigation item
3459     * @return {Roo.bootstrap.NavItem} the current navitem
3460     */
3461     getActive : function()
3462     {
3463         
3464         var prev = false;
3465         Roo.each(this.navItems, function(v){
3466             
3467             if (v.isActive()) {
3468                 prev = v;
3469                 
3470             }
3471             
3472         });
3473         return prev;
3474     },
3475     
3476     indexOfNav : function()
3477     {
3478         
3479         var prev = false;
3480         Roo.each(this.navItems, function(v,i){
3481             
3482             if (v.isActive()) {
3483                 prev = i;
3484                 
3485             }
3486             
3487         });
3488         return prev;
3489     },
3490     /**
3491     * adds a Navigation item
3492     * @param {Roo.bootstrap.NavItem} the navitem to add
3493     */
3494     addItem : function(cfg)
3495     {
3496         var cn = new Roo.bootstrap.NavItem(cfg);
3497         this.register(cn);
3498         cn.parentId = this.id;
3499         cn.onRender(this.el, null);
3500         return cn;
3501     },
3502     /**
3503     * register a Navigation item
3504     * @param {Roo.bootstrap.NavItem} the navitem to add
3505     */
3506     register : function(item)
3507     {
3508         this.navItems.push( item);
3509         item.navId = this.navId;
3510     
3511     },
3512     
3513     /**
3514     * clear all the Navigation item
3515     */
3516    
3517     clearAll : function()
3518     {
3519         this.navItems = [];
3520         this.el.dom.innerHTML = '';
3521     },
3522     
3523     getNavItem: function(tabId)
3524     {
3525         var ret = false;
3526         Roo.each(this.navItems, function(e) {
3527             if (e.tabId == tabId) {
3528                ret =  e;
3529                return false;
3530             }
3531             return true;
3532             
3533         });
3534         return ret;
3535     },
3536     
3537     setActiveNext : function()
3538     {
3539         var i = this.indexOfNav(this.getActive());
3540         if (i > this.navItems.length) {
3541             return;
3542         }
3543         this.setActiveItem(this.navItems[i+1]);
3544     },
3545     setActivePrev : function()
3546     {
3547         var i = this.indexOfNav(this.getActive());
3548         if (i  < 1) {
3549             return;
3550         }
3551         this.setActiveItem(this.navItems[i-1]);
3552     },
3553     clearWasActive : function(except) {
3554         Roo.each(this.navItems, function(e) {
3555             if (e.tabId != except.tabId && e.was_active) {
3556                e.was_active = false;
3557                return false;
3558             }
3559             return true;
3560             
3561         });
3562     },
3563     getWasActive : function ()
3564     {
3565         var r = false;
3566         Roo.each(this.navItems, function(e) {
3567             if (e.was_active) {
3568                r = e;
3569                return false;
3570             }
3571             return true;
3572             
3573         });
3574         return r;
3575     }
3576     
3577     
3578 });
3579
3580  
3581 Roo.apply(Roo.bootstrap.NavGroup, {
3582     
3583     groups: {},
3584      /**
3585     * register a Navigation Group
3586     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3587     */
3588     register : function(navgrp)
3589     {
3590         this.groups[navgrp.navId] = navgrp;
3591         
3592     },
3593     /**
3594     * fetch a Navigation Group based on the navigation ID
3595     * @param {string} the navgroup to add
3596     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3597     */
3598     get: function(navId) {
3599         if (typeof(this.groups[navId]) == 'undefined') {
3600             return false;
3601             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3602         }
3603         return this.groups[navId] ;
3604     }
3605     
3606     
3607     
3608 });
3609
3610  /*
3611  * - LGPL
3612  *
3613  * row
3614  * 
3615  */
3616
3617 /**
3618  * @class Roo.bootstrap.NavItem
3619  * @extends Roo.bootstrap.Component
3620  * Bootstrap Navbar.NavItem class
3621  * @cfg {String} href  link to
3622  * @cfg {String} html content of button
3623  * @cfg {String} badge text inside badge
3624  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3625  * @cfg {String} glyphicon name of glyphicon
3626  * @cfg {String} icon name of font awesome icon
3627  * @cfg {Boolean} active Is item active
3628  * @cfg {Boolean} disabled Is item disabled
3629  
3630  * @cfg {Boolean} preventDefault (true | false) default false
3631  * @cfg {String} tabId the tab that this item activates.
3632  * @cfg {String} tagtype (a|span) render as a href or span?
3633   
3634  * @constructor
3635  * Create a new Navbar Item
3636  * @param {Object} config The config object
3637  */
3638 Roo.bootstrap.NavItem = function(config){
3639     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3640     this.addEvents({
3641         // raw events
3642         /**
3643          * @event click
3644          * The raw click event for the entire grid.
3645          * @param {Roo.EventObject} e
3646          */
3647         "click" : true,
3648          /**
3649             * @event changed
3650             * Fires when the active item active state changes
3651             * @param {Roo.bootstrap.NavItem} this
3652             * @param {boolean} state the new state
3653              
3654          */
3655         'changed': true
3656     });
3657    
3658 };
3659
3660 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3661     
3662     href: false,
3663     html: '',
3664     badge: '',
3665     icon: false,
3666     glyphicon: false,
3667     active: false,
3668     preventDefault : false,
3669     tabId : false,
3670     tagtype : 'a',
3671     disabled : false,
3672     
3673     was_active : false,
3674     
3675     getAutoCreate : function(){
3676          
3677         var cfg = {
3678             tag: 'li',
3679             cls: 'nav-item'
3680             
3681         }
3682         if (this.active) {
3683             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3684         }
3685         if (this.disabled) {
3686             cfg.cls += ' disabled';
3687         }
3688         
3689         if (this.href || this.html || this.glyphicon || this.icon) {
3690             cfg.cn = [
3691                 {
3692                     tag: this.tagtype,
3693                     href : this.href || "#",
3694                     html: this.html || ''
3695                 }
3696             ];
3697             
3698             if (this.icon) {
3699                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3700             }
3701
3702             if(this.glyphicon) {
3703                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3704             }
3705             
3706             if (this.menu) {
3707                 
3708                 cfg.cn[0].html += " <span class='caret'></span>";
3709              
3710             }
3711             
3712             if (this.badge !== '') {
3713                  
3714                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3715             }
3716         }
3717         
3718         
3719         
3720         return cfg;
3721     },
3722     initEvents: function() {
3723        // Roo.log('init events?');
3724        // Roo.log(this.el.dom);
3725         if (typeof (this.menu) != 'undefined') {
3726             this.menu.parentType = this.xtype;
3727             this.menu.triggerEl = this.el;
3728             this.addxtype(Roo.apply({}, this.menu));
3729         }
3730
3731        
3732         this.el.select('a',true).on('click', this.onClick, this);
3733         // at this point parent should be available..
3734         this.parent().register(this);
3735     },
3736     
3737     onClick : function(e)
3738     {
3739          
3740         if(this.preventDefault){
3741             e.preventDefault();
3742         }
3743         if (this.disabled) {
3744             return;
3745         }
3746         
3747         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3748         if (tg && tg.transition) {
3749             Roo.log("waiting for the transitionend");
3750             return;
3751         }
3752         
3753         Roo.log("fire event clicked");
3754         if(this.fireEvent('click', this, e) === false){
3755             return;
3756         };
3757         var p = this.parent();
3758         if (['tabs','pills'].indexOf(p.type)!==-1) {
3759             if (typeof(p.setActiveItem) !== 'undefined') {
3760                 p.setActiveItem(this);
3761             }
3762         }
3763         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3764         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3765             // remove the collapsed menu expand...
3766             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3767         }
3768         
3769     },
3770     
3771     isActive: function () {
3772         return this.active
3773     },
3774     setActive : function(state, fire, is_was_active)
3775     {
3776         if (this.active && !state & this.navId) {
3777             this.was_active = true;
3778             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3779             if (nv) {
3780                 nv.clearWasActive(this);
3781             }
3782             
3783         }
3784         this.active = state;
3785         
3786         if (!state ) {
3787             this.el.removeClass('active');
3788         } else if (!this.el.hasClass('active')) {
3789             this.el.addClass('active');
3790         }
3791         if (fire) {
3792             this.fireEvent('changed', this, state);
3793         }
3794         
3795         // show a panel if it's registered and related..
3796         
3797         if (!this.navId || !this.tabId || !state || is_was_active) {
3798             return;
3799         }
3800         
3801         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3802         if (!tg) {
3803             return;
3804         }
3805         var pan = tg.getPanelByName(this.tabId);
3806         if (!pan) {
3807             return;
3808         }
3809         // if we can not flip to new panel - go back to old nav highlight..
3810         if (false == tg.showPanel(pan)) {
3811             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3812             if (nv) {
3813                 var onav = nv.getWasActive();
3814                 if (onav) {
3815                     onav.setActive(true, false, true);
3816                 }
3817             }
3818             
3819         }
3820         
3821         
3822         
3823     },
3824      // this should not be here...
3825     setDisabled : function(state)
3826     {
3827         this.disabled = state;
3828         if (!state ) {
3829             this.el.removeClass('disabled');
3830         } else if (!this.el.hasClass('disabled')) {
3831             this.el.addClass('disabled');
3832         }
3833         
3834     }
3835 });
3836  
3837
3838  /*
3839  * - LGPL
3840  *
3841  * sidebar item
3842  *
3843  *  li
3844  *    <span> icon </span>
3845  *    <span> text </span>
3846  *    <span>badge </span>
3847  */
3848
3849 /**
3850  * @class Roo.bootstrap.NavSidebarItem
3851  * @extends Roo.bootstrap.NavItem
3852  * Bootstrap Navbar.NavSidebarItem class
3853  * @constructor
3854  * Create a new Navbar Button
3855  * @param {Object} config The config object
3856  */
3857 Roo.bootstrap.NavSidebarItem = function(config){
3858     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3859     this.addEvents({
3860         // raw events
3861         /**
3862          * @event click
3863          * The raw click event for the entire grid.
3864          * @param {Roo.EventObject} e
3865          */
3866         "click" : true,
3867          /**
3868             * @event changed
3869             * Fires when the active item active state changes
3870             * @param {Roo.bootstrap.NavSidebarItem} this
3871             * @param {boolean} state the new state
3872              
3873          */
3874         'changed': true
3875     });
3876    
3877 };
3878
3879 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3880     
3881     
3882     getAutoCreate : function(){
3883         
3884         
3885         var a = {
3886                 tag: 'a',
3887                 href : this.href || '#',
3888                 cls: '',
3889                 html : '',
3890                 cn : []
3891         };
3892         var cfg = {
3893             tag: 'li',
3894             cls: '',
3895             cn: [ a ]
3896         }
3897         var span = {
3898             tag: 'span',
3899             html : this.html || ''
3900         }
3901         
3902         
3903         if (this.active) {
3904             cfg.cls += ' active';
3905         }
3906         
3907         // left icon..
3908         if (this.glyphicon || this.icon) {
3909             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3910             a.cn.push({ tag : 'i', cls : c }) ;
3911         }
3912         // html..
3913         a.cn.push(span);
3914         // then badge..
3915         if (this.badge !== '') {
3916             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3917         }
3918         // fi
3919         if (this.menu) {
3920             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3921             a.cls += 'dropdown-toggle treeview' ;
3922             
3923         }
3924         
3925         
3926         
3927         return cfg;
3928          
3929            
3930     }
3931    
3932      
3933  
3934 });
3935  
3936
3937  /*
3938  * - LGPL
3939  *
3940  * row
3941  * 
3942  */
3943
3944 /**
3945  * @class Roo.bootstrap.Row
3946  * @extends Roo.bootstrap.Component
3947  * Bootstrap Row class (contains columns...)
3948  * 
3949  * @constructor
3950  * Create a new Row
3951  * @param {Object} config The config object
3952  */
3953
3954 Roo.bootstrap.Row = function(config){
3955     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3956 };
3957
3958 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3959     
3960     getAutoCreate : function(){
3961        return {
3962             cls: 'row clearfix'
3963        };
3964     }
3965     
3966     
3967 });
3968
3969  
3970
3971  /*
3972  * - LGPL
3973  *
3974  * element
3975  * 
3976  */
3977
3978 /**
3979  * @class Roo.bootstrap.Element
3980  * @extends Roo.bootstrap.Component
3981  * Bootstrap Element class
3982  * @cfg {String} html contents of the element
3983  * @cfg {String} tag tag of the element
3984  * @cfg {String} cls class of the element
3985  * 
3986  * @constructor
3987  * Create a new Element
3988  * @param {Object} config The config object
3989  */
3990
3991 Roo.bootstrap.Element = function(config){
3992     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3993 };
3994
3995 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3996     
3997     tag: 'div',
3998     cls: '',
3999     html: '',
4000      
4001     
4002     getAutoCreate : function(){
4003         
4004         var cfg = {
4005             tag: this.tag,
4006             cls: this.cls,
4007             html: this.html
4008         }
4009         
4010         
4011         
4012         return cfg;
4013     }
4014    
4015 });
4016
4017  
4018
4019  /*
4020  * - LGPL
4021  *
4022  * pagination
4023  * 
4024  */
4025
4026 /**
4027  * @class Roo.bootstrap.Pagination
4028  * @extends Roo.bootstrap.Component
4029  * Bootstrap Pagination class
4030  * @cfg {String} size xs | sm | md | lg
4031  * @cfg {Boolean} inverse false | true
4032  * 
4033  * @constructor
4034  * Create a new Pagination
4035  * @param {Object} config The config object
4036  */
4037
4038 Roo.bootstrap.Pagination = function(config){
4039     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4040 };
4041
4042 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4043     
4044     cls: false,
4045     size: false,
4046     inverse: false,
4047     
4048     getAutoCreate : function(){
4049         var cfg = {
4050             tag: 'ul',
4051                 cls: 'pagination'
4052         };
4053         if (this.inverse) {
4054             cfg.cls += ' inverse';
4055         }
4056         if (this.html) {
4057             cfg.html=this.html;
4058         }
4059         if (this.cls) {
4060             cfg.cls += " " + this.cls;
4061         }
4062         return cfg;
4063     }
4064    
4065 });
4066
4067  
4068
4069  /*
4070  * - LGPL
4071  *
4072  * Pagination item
4073  * 
4074  */
4075
4076
4077 /**
4078  * @class Roo.bootstrap.PaginationItem
4079  * @extends Roo.bootstrap.Component
4080  * Bootstrap PaginationItem class
4081  * @cfg {String} html text
4082  * @cfg {String} href the link
4083  * @cfg {Boolean} preventDefault (true | false) default true
4084  * @cfg {Boolean} active (true | false) default false
4085  * 
4086  * 
4087  * @constructor
4088  * Create a new PaginationItem
4089  * @param {Object} config The config object
4090  */
4091
4092
4093 Roo.bootstrap.PaginationItem = function(config){
4094     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4095     this.addEvents({
4096         // raw events
4097         /**
4098          * @event click
4099          * The raw click event for the entire grid.
4100          * @param {Roo.EventObject} e
4101          */
4102         "click" : true
4103     });
4104 };
4105
4106 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4107     
4108     href : false,
4109     html : false,
4110     preventDefault: true,
4111     active : false,
4112     cls : false,
4113     
4114     getAutoCreate : function(){
4115         var cfg= {
4116             tag: 'li',
4117             cn: [
4118                 {
4119                     tag : 'a',
4120                     href : this.href ? this.href : '#',
4121                     html : this.html ? this.html : ''
4122                 }
4123             ]
4124         };
4125         
4126         if(this.cls){
4127             cfg.cls = this.cls;
4128         }
4129         
4130         if(this.active){
4131             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4132         }
4133         
4134         return cfg;
4135     },
4136     
4137     initEvents: function() {
4138         
4139         this.el.on('click', this.onClick, this);
4140         
4141     },
4142     onClick : function(e)
4143     {
4144         Roo.log('PaginationItem on click ');
4145         if(this.preventDefault){
4146             e.preventDefault();
4147         }
4148         
4149         this.fireEvent('click', this, e);
4150     }
4151    
4152 });
4153
4154  
4155
4156  /*
4157  * - LGPL
4158  *
4159  * slider
4160  * 
4161  */
4162
4163
4164 /**
4165  * @class Roo.bootstrap.Slider
4166  * @extends Roo.bootstrap.Component
4167  * Bootstrap Slider class
4168  *    
4169  * @constructor
4170  * Create a new Slider
4171  * @param {Object} config The config object
4172  */
4173
4174 Roo.bootstrap.Slider = function(config){
4175     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4176 };
4177
4178 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4179     
4180     getAutoCreate : function(){
4181         
4182         var cfg = {
4183             tag: 'div',
4184             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4185             cn: [
4186                 {
4187                     tag: 'a',
4188                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4189                 }
4190             ]
4191         }
4192         
4193         return cfg;
4194     }
4195    
4196 });
4197
4198  /*
4199  * Based on:
4200  * Ext JS Library 1.1.1
4201  * Copyright(c) 2006-2007, Ext JS, LLC.
4202  *
4203  * Originally Released Under LGPL - original licence link has changed is not relivant.
4204  *
4205  * Fork - LGPL
4206  * <script type="text/javascript">
4207  */
4208  
4209
4210 /**
4211  * @class Roo.grid.ColumnModel
4212  * @extends Roo.util.Observable
4213  * This is the default implementation of a ColumnModel used by the Grid. It defines
4214  * the columns in the grid.
4215  * <br>Usage:<br>
4216  <pre><code>
4217  var colModel = new Roo.grid.ColumnModel([
4218         {header: "Ticker", width: 60, sortable: true, locked: true},
4219         {header: "Company Name", width: 150, sortable: true},
4220         {header: "Market Cap.", width: 100, sortable: true},
4221         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4222         {header: "Employees", width: 100, sortable: true, resizable: false}
4223  ]);
4224  </code></pre>
4225  * <p>
4226  
4227  * The config options listed for this class are options which may appear in each
4228  * individual column definition.
4229  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4230  * @constructor
4231  * @param {Object} config An Array of column config objects. See this class's
4232  * config objects for details.
4233 */
4234 Roo.grid.ColumnModel = function(config){
4235         /**
4236      * The config passed into the constructor
4237      */
4238     this.config = config;
4239     this.lookup = {};
4240
4241     // if no id, create one
4242     // if the column does not have a dataIndex mapping,
4243     // map it to the order it is in the config
4244     for(var i = 0, len = config.length; i < len; i++){
4245         var c = config[i];
4246         if(typeof c.dataIndex == "undefined"){
4247             c.dataIndex = i;
4248         }
4249         if(typeof c.renderer == "string"){
4250             c.renderer = Roo.util.Format[c.renderer];
4251         }
4252         if(typeof c.id == "undefined"){
4253             c.id = Roo.id();
4254         }
4255         if(c.editor && c.editor.xtype){
4256             c.editor  = Roo.factory(c.editor, Roo.grid);
4257         }
4258         if(c.editor && c.editor.isFormField){
4259             c.editor = new Roo.grid.GridEditor(c.editor);
4260         }
4261         this.lookup[c.id] = c;
4262     }
4263
4264     /**
4265      * The width of columns which have no width specified (defaults to 100)
4266      * @type Number
4267      */
4268     this.defaultWidth = 100;
4269
4270     /**
4271      * Default sortable of columns which have no sortable specified (defaults to false)
4272      * @type Boolean
4273      */
4274     this.defaultSortable = false;
4275
4276     this.addEvents({
4277         /**
4278              * @event widthchange
4279              * Fires when the width of a column changes.
4280              * @param {ColumnModel} this
4281              * @param {Number} columnIndex The column index
4282              * @param {Number} newWidth The new width
4283              */
4284             "widthchange": true,
4285         /**
4286              * @event headerchange
4287              * Fires when the text of a header changes.
4288              * @param {ColumnModel} this
4289              * @param {Number} columnIndex The column index
4290              * @param {Number} newText The new header text
4291              */
4292             "headerchange": true,
4293         /**
4294              * @event hiddenchange
4295              * Fires when a column is hidden or "unhidden".
4296              * @param {ColumnModel} this
4297              * @param {Number} columnIndex The column index
4298              * @param {Boolean} hidden true if hidden, false otherwise
4299              */
4300             "hiddenchange": true,
4301             /**
4302          * @event columnmoved
4303          * Fires when a column is moved.
4304          * @param {ColumnModel} this
4305          * @param {Number} oldIndex
4306          * @param {Number} newIndex
4307          */
4308         "columnmoved" : true,
4309         /**
4310          * @event columlockchange
4311          * Fires when a column's locked state is changed
4312          * @param {ColumnModel} this
4313          * @param {Number} colIndex
4314          * @param {Boolean} locked true if locked
4315          */
4316         "columnlockchange" : true
4317     });
4318     Roo.grid.ColumnModel.superclass.constructor.call(this);
4319 };
4320 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4321     /**
4322      * @cfg {String} header The header text to display in the Grid view.
4323      */
4324     /**
4325      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4326      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4327      * specified, the column's index is used as an index into the Record's data Array.
4328      */
4329     /**
4330      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4331      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4332      */
4333     /**
4334      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4335      * Defaults to the value of the {@link #defaultSortable} property.
4336      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4337      */
4338     /**
4339      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4340      */
4341     /**
4342      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4343      */
4344     /**
4345      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4346      */
4347     /**
4348      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4349      */
4350     /**
4351      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4352      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4353      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4354      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4355      */
4356        /**
4357      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4358      */
4359     /**
4360      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4361      */
4362
4363     /**
4364      * Returns the id of the column at the specified index.
4365      * @param {Number} index The column index
4366      * @return {String} the id
4367      */
4368     getColumnId : function(index){
4369         return this.config[index].id;
4370     },
4371
4372     /**
4373      * Returns the column for a specified id.
4374      * @param {String} id The column id
4375      * @return {Object} the column
4376      */
4377     getColumnById : function(id){
4378         return this.lookup[id];
4379     },
4380
4381     
4382     /**
4383      * Returns the column for a specified dataIndex.
4384      * @param {String} dataIndex The column dataIndex
4385      * @return {Object|Boolean} the column or false if not found
4386      */
4387     getColumnByDataIndex: function(dataIndex){
4388         var index = this.findColumnIndex(dataIndex);
4389         return index > -1 ? this.config[index] : false;
4390     },
4391     
4392     /**
4393      * Returns the index for a specified column id.
4394      * @param {String} id The column id
4395      * @return {Number} the index, or -1 if not found
4396      */
4397     getIndexById : function(id){
4398         for(var i = 0, len = this.config.length; i < len; i++){
4399             if(this.config[i].id == id){
4400                 return i;
4401             }
4402         }
4403         return -1;
4404     },
4405     
4406     /**
4407      * Returns the index for a specified column dataIndex.
4408      * @param {String} dataIndex The column dataIndex
4409      * @return {Number} the index, or -1 if not found
4410      */
4411     
4412     findColumnIndex : function(dataIndex){
4413         for(var i = 0, len = this.config.length; i < len; i++){
4414             if(this.config[i].dataIndex == dataIndex){
4415                 return i;
4416             }
4417         }
4418         return -1;
4419     },
4420     
4421     
4422     moveColumn : function(oldIndex, newIndex){
4423         var c = this.config[oldIndex];
4424         this.config.splice(oldIndex, 1);
4425         this.config.splice(newIndex, 0, c);
4426         this.dataMap = null;
4427         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4428     },
4429
4430     isLocked : function(colIndex){
4431         return this.config[colIndex].locked === true;
4432     },
4433
4434     setLocked : function(colIndex, value, suppressEvent){
4435         if(this.isLocked(colIndex) == value){
4436             return;
4437         }
4438         this.config[colIndex].locked = value;
4439         if(!suppressEvent){
4440             this.fireEvent("columnlockchange", this, colIndex, value);
4441         }
4442     },
4443
4444     getTotalLockedWidth : function(){
4445         var totalWidth = 0;
4446         for(var i = 0; i < this.config.length; i++){
4447             if(this.isLocked(i) && !this.isHidden(i)){
4448                 this.totalWidth += this.getColumnWidth(i);
4449             }
4450         }
4451         return totalWidth;
4452     },
4453
4454     getLockedCount : function(){
4455         for(var i = 0, len = this.config.length; i < len; i++){
4456             if(!this.isLocked(i)){
4457                 return i;
4458             }
4459         }
4460     },
4461
4462     /**
4463      * Returns the number of columns.
4464      * @return {Number}
4465      */
4466     getColumnCount : function(visibleOnly){
4467         if(visibleOnly === true){
4468             var c = 0;
4469             for(var i = 0, len = this.config.length; i < len; i++){
4470                 if(!this.isHidden(i)){
4471                     c++;
4472                 }
4473             }
4474             return c;
4475         }
4476         return this.config.length;
4477     },
4478
4479     /**
4480      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4481      * @param {Function} fn
4482      * @param {Object} scope (optional)
4483      * @return {Array} result
4484      */
4485     getColumnsBy : function(fn, scope){
4486         var r = [];
4487         for(var i = 0, len = this.config.length; i < len; i++){
4488             var c = this.config[i];
4489             if(fn.call(scope||this, c, i) === true){
4490                 r[r.length] = c;
4491             }
4492         }
4493         return r;
4494     },
4495
4496     /**
4497      * Returns true if the specified column is sortable.
4498      * @param {Number} col The column index
4499      * @return {Boolean}
4500      */
4501     isSortable : function(col){
4502         if(typeof this.config[col].sortable == "undefined"){
4503             return this.defaultSortable;
4504         }
4505         return this.config[col].sortable;
4506     },
4507
4508     /**
4509      * Returns the rendering (formatting) function defined for the column.
4510      * @param {Number} col The column index.
4511      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4512      */
4513     getRenderer : function(col){
4514         if(!this.config[col].renderer){
4515             return Roo.grid.ColumnModel.defaultRenderer;
4516         }
4517         return this.config[col].renderer;
4518     },
4519
4520     /**
4521      * Sets the rendering (formatting) function for a column.
4522      * @param {Number} col The column index
4523      * @param {Function} fn The function to use to process the cell's raw data
4524      * to return HTML markup for the grid view. The render function is called with
4525      * the following parameters:<ul>
4526      * <li>Data value.</li>
4527      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4528      * <li>css A CSS style string to apply to the table cell.</li>
4529      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4530      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4531      * <li>Row index</li>
4532      * <li>Column index</li>
4533      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4534      */
4535     setRenderer : function(col, fn){
4536         this.config[col].renderer = fn;
4537     },
4538
4539     /**
4540      * Returns the width for the specified column.
4541      * @param {Number} col The column index
4542      * @return {Number}
4543      */
4544     getColumnWidth : function(col){
4545         return this.config[col].width * 1 || this.defaultWidth;
4546     },
4547
4548     /**
4549      * Sets the width for a column.
4550      * @param {Number} col The column index
4551      * @param {Number} width The new width
4552      */
4553     setColumnWidth : function(col, width, suppressEvent){
4554         this.config[col].width = width;
4555         this.totalWidth = null;
4556         if(!suppressEvent){
4557              this.fireEvent("widthchange", this, col, width);
4558         }
4559     },
4560
4561     /**
4562      * Returns the total width of all columns.
4563      * @param {Boolean} includeHidden True to include hidden column widths
4564      * @return {Number}
4565      */
4566     getTotalWidth : function(includeHidden){
4567         if(!this.totalWidth){
4568             this.totalWidth = 0;
4569             for(var i = 0, len = this.config.length; i < len; i++){
4570                 if(includeHidden || !this.isHidden(i)){
4571                     this.totalWidth += this.getColumnWidth(i);
4572                 }
4573             }
4574         }
4575         return this.totalWidth;
4576     },
4577
4578     /**
4579      * Returns the header for the specified column.
4580      * @param {Number} col The column index
4581      * @return {String}
4582      */
4583     getColumnHeader : function(col){
4584         return this.config[col].header;
4585     },
4586
4587     /**
4588      * Sets the header for a column.
4589      * @param {Number} col The column index
4590      * @param {String} header The new header
4591      */
4592     setColumnHeader : function(col, header){
4593         this.config[col].header = header;
4594         this.fireEvent("headerchange", this, col, header);
4595     },
4596
4597     /**
4598      * Returns the tooltip for the specified column.
4599      * @param {Number} col The column index
4600      * @return {String}
4601      */
4602     getColumnTooltip : function(col){
4603             return this.config[col].tooltip;
4604     },
4605     /**
4606      * Sets the tooltip for a column.
4607      * @param {Number} col The column index
4608      * @param {String} tooltip The new tooltip
4609      */
4610     setColumnTooltip : function(col, tooltip){
4611             this.config[col].tooltip = tooltip;
4612     },
4613
4614     /**
4615      * Returns the dataIndex for the specified column.
4616      * @param {Number} col The column index
4617      * @return {Number}
4618      */
4619     getDataIndex : function(col){
4620         return this.config[col].dataIndex;
4621     },
4622
4623     /**
4624      * Sets the dataIndex for a column.
4625      * @param {Number} col The column index
4626      * @param {Number} dataIndex The new dataIndex
4627      */
4628     setDataIndex : function(col, dataIndex){
4629         this.config[col].dataIndex = dataIndex;
4630     },
4631
4632     
4633     
4634     /**
4635      * Returns true if the cell is editable.
4636      * @param {Number} colIndex The column index
4637      * @param {Number} rowIndex The row index
4638      * @return {Boolean}
4639      */
4640     isCellEditable : function(colIndex, rowIndex){
4641         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4642     },
4643
4644     /**
4645      * Returns the editor defined for the cell/column.
4646      * return false or null to disable editing.
4647      * @param {Number} colIndex The column index
4648      * @param {Number} rowIndex The row index
4649      * @return {Object}
4650      */
4651     getCellEditor : function(colIndex, rowIndex){
4652         return this.config[colIndex].editor;
4653     },
4654
4655     /**
4656      * Sets if a column is editable.
4657      * @param {Number} col The column index
4658      * @param {Boolean} editable True if the column is editable
4659      */
4660     setEditable : function(col, editable){
4661         this.config[col].editable = editable;
4662     },
4663
4664
4665     /**
4666      * Returns true if the column is hidden.
4667      * @param {Number} colIndex The column index
4668      * @return {Boolean}
4669      */
4670     isHidden : function(colIndex){
4671         return this.config[colIndex].hidden;
4672     },
4673
4674
4675     /**
4676      * Returns true if the column width cannot be changed
4677      */
4678     isFixed : function(colIndex){
4679         return this.config[colIndex].fixed;
4680     },
4681
4682     /**
4683      * Returns true if the column can be resized
4684      * @return {Boolean}
4685      */
4686     isResizable : function(colIndex){
4687         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4688     },
4689     /**
4690      * Sets if a column is hidden.
4691      * @param {Number} colIndex The column index
4692      * @param {Boolean} hidden True if the column is hidden
4693      */
4694     setHidden : function(colIndex, hidden){
4695         this.config[colIndex].hidden = hidden;
4696         this.totalWidth = null;
4697         this.fireEvent("hiddenchange", this, colIndex, hidden);
4698     },
4699
4700     /**
4701      * Sets the editor for a column.
4702      * @param {Number} col The column index
4703      * @param {Object} editor The editor object
4704      */
4705     setEditor : function(col, editor){
4706         this.config[col].editor = editor;
4707     }
4708 });
4709
4710 Roo.grid.ColumnModel.defaultRenderer = function(value){
4711         if(typeof value == "string" && value.length < 1){
4712             return "&#160;";
4713         }
4714         return value;
4715 };
4716
4717 // Alias for backwards compatibility
4718 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4719 /*
4720  * Based on:
4721  * Ext JS Library 1.1.1
4722  * Copyright(c) 2006-2007, Ext JS, LLC.
4723  *
4724  * Originally Released Under LGPL - original licence link has changed is not relivant.
4725  *
4726  * Fork - LGPL
4727  * <script type="text/javascript">
4728  */
4729  
4730 /**
4731  * @class Roo.LoadMask
4732  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4733  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4734  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4735  * element's UpdateManager load indicator and will be destroyed after the initial load.
4736  * @constructor
4737  * Create a new LoadMask
4738  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4739  * @param {Object} config The config object
4740  */
4741 Roo.LoadMask = function(el, config){
4742     this.el = Roo.get(el);
4743     Roo.apply(this, config);
4744     if(this.store){
4745         this.store.on('beforeload', this.onBeforeLoad, this);
4746         this.store.on('load', this.onLoad, this);
4747         this.store.on('loadexception', this.onLoadException, this);
4748         this.removeMask = false;
4749     }else{
4750         var um = this.el.getUpdateManager();
4751         um.showLoadIndicator = false; // disable the default indicator
4752         um.on('beforeupdate', this.onBeforeLoad, this);
4753         um.on('update', this.onLoad, this);
4754         um.on('failure', this.onLoad, this);
4755         this.removeMask = true;
4756     }
4757 };
4758
4759 Roo.LoadMask.prototype = {
4760     /**
4761      * @cfg {Boolean} removeMask
4762      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4763      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4764      */
4765     /**
4766      * @cfg {String} msg
4767      * The text to display in a centered loading message box (defaults to 'Loading...')
4768      */
4769     msg : 'Loading...',
4770     /**
4771      * @cfg {String} msgCls
4772      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4773      */
4774     msgCls : 'x-mask-loading',
4775
4776     /**
4777      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4778      * @type Boolean
4779      */
4780     disabled: false,
4781
4782     /**
4783      * Disables the mask to prevent it from being displayed
4784      */
4785     disable : function(){
4786        this.disabled = true;
4787     },
4788
4789     /**
4790      * Enables the mask so that it can be displayed
4791      */
4792     enable : function(){
4793         this.disabled = false;
4794     },
4795     
4796     onLoadException : function()
4797     {
4798         Roo.log(arguments);
4799         
4800         if (typeof(arguments[3]) != 'undefined') {
4801             Roo.MessageBox.alert("Error loading",arguments[3]);
4802         } 
4803         /*
4804         try {
4805             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4806                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4807             }   
4808         } catch(e) {
4809             
4810         }
4811         */
4812     
4813         
4814         
4815         this.el.unmask(this.removeMask);
4816     },
4817     // private
4818     onLoad : function()
4819     {
4820         this.el.unmask(this.removeMask);
4821     },
4822
4823     // private
4824     onBeforeLoad : function(){
4825         if(!this.disabled){
4826             this.el.mask(this.msg, this.msgCls);
4827         }
4828     },
4829
4830     // private
4831     destroy : function(){
4832         if(this.store){
4833             this.store.un('beforeload', this.onBeforeLoad, this);
4834             this.store.un('load', this.onLoad, this);
4835             this.store.un('loadexception', this.onLoadException, this);
4836         }else{
4837             var um = this.el.getUpdateManager();
4838             um.un('beforeupdate', this.onBeforeLoad, this);
4839             um.un('update', this.onLoad, this);
4840             um.un('failure', this.onLoad, this);
4841         }
4842     }
4843 };/*
4844  * - LGPL
4845  *
4846  * table
4847  * 
4848  */
4849
4850 /**
4851  * @class Roo.bootstrap.Table
4852  * @extends Roo.bootstrap.Component
4853  * Bootstrap Table class
4854  * @cfg {String} cls table class
4855  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4856  * @cfg {String} bgcolor Specifies the background color for a table
4857  * @cfg {Number} border Specifies whether the table cells should have borders or not
4858  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4859  * @cfg {Number} cellspacing Specifies the space between cells
4860  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4861  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4862  * @cfg {String} sortable Specifies that the table should be sortable
4863  * @cfg {String} summary Specifies a summary of the content of a table
4864  * @cfg {Number} width Specifies the width of a table
4865  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4866  * 
4867  * @cfg {boolean} striped Should the rows be alternative striped
4868  * @cfg {boolean} bordered Add borders to the table
4869  * @cfg {boolean} hover Add hover highlighting
4870  * @cfg {boolean} condensed Format condensed
4871  * @cfg {boolean} responsive Format condensed
4872  * @cfg {Boolean} loadMask (true|false) default false
4873  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4874  * @cfg {Boolean} thead (true|false) generate thead, default true
4875  * @cfg {Boolean} RowSelection (true|false) default false
4876  * @cfg {Boolean} CellSelection (true|false) default false
4877  *
4878  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4879  
4880  * 
4881  * @constructor
4882  * Create a new Table
4883  * @param {Object} config The config object
4884  */
4885
4886 Roo.bootstrap.Table = function(config){
4887     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4888     
4889     if (this.sm) {
4890         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4891         this.sm = this.selModel;
4892         this.sm.xmodule = this.xmodule || false;
4893     }
4894     if (this.cm && typeof(this.cm.config) == 'undefined') {
4895         this.colModel = new Roo.grid.ColumnModel(this.cm);
4896         this.cm = this.colModel;
4897         this.cm.xmodule = this.xmodule || false;
4898     }
4899     if (this.store) {
4900         this.store= Roo.factory(this.store, Roo.data);
4901         this.ds = this.store;
4902         this.ds.xmodule = this.xmodule || false;
4903          
4904     }
4905     if (this.footer && this.store) {
4906         this.footer.dataSource = this.ds;
4907         this.footer = Roo.factory(this.footer);
4908     }
4909     
4910     /** @private */
4911     this.addEvents({
4912         /**
4913          * @event cellclick
4914          * Fires when a cell is clicked
4915          * @param {Roo.bootstrap.Table} this
4916          * @param {Roo.Element} el
4917          * @param {Number} rowIndex
4918          * @param {Number} columnIndex
4919          * @param {Roo.EventObject} e
4920          */
4921         "cellclick" : true,
4922         /**
4923          * @event celldblclick
4924          * Fires when a cell is double clicked
4925          * @param {Roo.bootstrap.Table} this
4926          * @param {Roo.Element} el
4927          * @param {Number} rowIndex
4928          * @param {Number} columnIndex
4929          * @param {Roo.EventObject} e
4930          */
4931         "celldblclick" : true,
4932         /**
4933          * @event rowclick
4934          * Fires when a row is clicked
4935          * @param {Roo.bootstrap.Table} this
4936          * @param {Roo.Element} el
4937          * @param {Number} rowIndex
4938          * @param {Roo.EventObject} e
4939          */
4940         "rowclick" : true,
4941         /**
4942          * @event rowdblclick
4943          * Fires when a row is double clicked
4944          * @param {Roo.bootstrap.Table} this
4945          * @param {Roo.Element} el
4946          * @param {Number} rowIndex
4947          * @param {Roo.EventObject} e
4948          */
4949         "rowdblclick" : true,
4950         /**
4951          * @event mouseover
4952          * Fires when a mouseover occur
4953          * @param {Roo.bootstrap.Table} this
4954          * @param {Roo.Element} el
4955          * @param {Number} rowIndex
4956          * @param {Number} columnIndex
4957          * @param {Roo.EventObject} e
4958          */
4959         "mouseover" : true,
4960         /**
4961          * @event mouseout
4962          * Fires when a mouseout occur
4963          * @param {Roo.bootstrap.Table} this
4964          * @param {Roo.Element} el
4965          * @param {Number} rowIndex
4966          * @param {Number} columnIndex
4967          * @param {Roo.EventObject} e
4968          */
4969         "mouseout" : true,
4970         /**
4971          * @event rowclass
4972          * Fires when a row is rendered, so you can change add a style to it.
4973          * @param {Roo.bootstrap.Table} this
4974          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4975          */
4976         'rowclass' : true
4977         
4978     });
4979 };
4980
4981 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4982     
4983     cls: false,
4984     align: false,
4985     bgcolor: false,
4986     border: false,
4987     cellpadding: false,
4988     cellspacing: false,
4989     frame: false,
4990     rules: false,
4991     sortable: false,
4992     summary: false,
4993     width: false,
4994     striped : false,
4995     bordered: false,
4996     hover:  false,
4997     condensed : false,
4998     responsive : false,
4999     sm : false,
5000     cm : false,
5001     store : false,
5002     loadMask : false,
5003     tfoot : true,
5004     thead : true,
5005     RowSelection : false,
5006     CellSelection : false,
5007     layout : false,
5008     
5009     // Roo.Element - the tbody
5010     mainBody: false, 
5011     
5012     getAutoCreate : function(){
5013         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5014         
5015         cfg = {
5016             tag: 'table',
5017             cls : 'table',
5018             cn : []
5019         }
5020             
5021         if (this.striped) {
5022             cfg.cls += ' table-striped';
5023         }
5024         
5025         if (this.hover) {
5026             cfg.cls += ' table-hover';
5027         }
5028         if (this.bordered) {
5029             cfg.cls += ' table-bordered';
5030         }
5031         if (this.condensed) {
5032             cfg.cls += ' table-condensed';
5033         }
5034         if (this.responsive) {
5035             cfg.cls += ' table-responsive';
5036         }
5037         
5038         if (this.cls) {
5039             cfg.cls+=  ' ' +this.cls;
5040         }
5041         
5042         // this lot should be simplifed...
5043         
5044         if (this.align) {
5045             cfg.align=this.align;
5046         }
5047         if (this.bgcolor) {
5048             cfg.bgcolor=this.bgcolor;
5049         }
5050         if (this.border) {
5051             cfg.border=this.border;
5052         }
5053         if (this.cellpadding) {
5054             cfg.cellpadding=this.cellpadding;
5055         }
5056         if (this.cellspacing) {
5057             cfg.cellspacing=this.cellspacing;
5058         }
5059         if (this.frame) {
5060             cfg.frame=this.frame;
5061         }
5062         if (this.rules) {
5063             cfg.rules=this.rules;
5064         }
5065         if (this.sortable) {
5066             cfg.sortable=this.sortable;
5067         }
5068         if (this.summary) {
5069             cfg.summary=this.summary;
5070         }
5071         if (this.width) {
5072             cfg.width=this.width;
5073         }
5074         if (this.layout) {
5075             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5076         }
5077         
5078         if(this.store || this.cm){
5079             if(this.thead){
5080                 cfg.cn.push(this.renderHeader());
5081             }
5082             
5083             cfg.cn.push(this.renderBody());
5084             
5085             if(this.tfoot){
5086                 cfg.cn.push(this.renderFooter());
5087             }
5088             
5089             cfg.cls+=  ' TableGrid';
5090         }
5091         
5092         return { cn : [ cfg ] };
5093     },
5094     
5095     initEvents : function()
5096     {   
5097         if(!this.store || !this.cm){
5098             return;
5099         }
5100         
5101         //Roo.log('initEvents with ds!!!!');
5102         
5103         this.mainBody = this.el.select('tbody', true).first();
5104         
5105         
5106         var _this = this;
5107         
5108         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5109             e.on('click', _this.sort, _this);
5110         });
5111         
5112         this.el.on("click", this.onClick, this);
5113         this.el.on("dblclick", this.onDblClick, this);
5114         
5115         this.parent().el.setStyle('position', 'relative');
5116         if (this.footer) {
5117             this.footer.parentId = this.id;
5118             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5119         }
5120         
5121         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5122         
5123         this.store.on('load', this.onLoad, this);
5124         this.store.on('beforeload', this.onBeforeLoad, this);
5125         this.store.on('update', this.onUpdate, this);
5126         
5127     },
5128     
5129     onMouseover : function(e, el)
5130     {
5131         var cell = Roo.get(el);
5132         
5133         if(!cell){
5134             return;
5135         }
5136         
5137         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5138             cell = cell.findParent('td', false, true);
5139         }
5140         
5141         var row = cell.findParent('tr', false, true);
5142         var cellIndex = cell.dom.cellIndex;
5143         var rowIndex = row.dom.rowIndex - 1; // start from 0
5144         
5145         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5146         
5147     },
5148     
5149     onMouseout : function(e, el)
5150     {
5151         var cell = Roo.get(el);
5152         
5153         if(!cell){
5154             return;
5155         }
5156         
5157         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5158             cell = cell.findParent('td', false, true);
5159         }
5160         
5161         var row = cell.findParent('tr', false, true);
5162         var cellIndex = cell.dom.cellIndex;
5163         var rowIndex = row.dom.rowIndex - 1; // start from 0
5164         
5165         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5166         
5167     },
5168     
5169     onClick : function(e, el)
5170     {
5171         var cell = Roo.get(el);
5172         
5173         if(!cell || (!this.CellSelection && !this.RowSelection)){
5174             return;
5175         }
5176         
5177         
5178         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5179             cell = cell.findParent('td', false, true);
5180         }
5181         
5182         var row = cell.findParent('tr', false, true);
5183         var cellIndex = cell.dom.cellIndex;
5184         var rowIndex = row.dom.rowIndex - 1;
5185         
5186         if(this.CellSelection){
5187             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5188         }
5189         
5190         if(this.RowSelection){
5191             this.fireEvent('rowclick', this, row, rowIndex, e);
5192         }
5193         
5194         
5195     },
5196     
5197     onDblClick : function(e,el)
5198     {
5199         var cell = Roo.get(el);
5200         
5201         if(!cell || (!this.CellSelection && !this.RowSelection)){
5202             return;
5203         }
5204         
5205         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5206             cell = cell.findParent('td', false, true);
5207         }
5208         
5209         var row = cell.findParent('tr', false, true);
5210         var cellIndex = cell.dom.cellIndex;
5211         var rowIndex = row.dom.rowIndex - 1;
5212         
5213         if(this.CellSelection){
5214             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5215         }
5216         
5217         if(this.RowSelection){
5218             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5219         }
5220     },
5221     
5222     sort : function(e,el)
5223     {
5224         var col = Roo.get(el)
5225         
5226         if(!col.hasClass('sortable')){
5227             return;
5228         }
5229         
5230         var sort = col.attr('sort');
5231         var dir = 'ASC';
5232         
5233         if(col.hasClass('glyphicon-arrow-up')){
5234             dir = 'DESC';
5235         }
5236         
5237         this.store.sortInfo = {field : sort, direction : dir};
5238         
5239         if (this.footer) {
5240             Roo.log("calling footer first");
5241             this.footer.onClick('first');
5242         } else {
5243         
5244             this.store.load({ params : { start : 0 } });
5245         }
5246     },
5247     
5248     renderHeader : function()
5249     {
5250         var header = {
5251             tag: 'thead',
5252             cn : []
5253         };
5254         
5255         var cm = this.cm;
5256         
5257         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5258             
5259             var config = cm.config[i];
5260                     
5261             var c = {
5262                 tag: 'th',
5263                 style : '',
5264                 html: cm.getColumnHeader(i)
5265             };
5266             
5267             if(typeof(config.hidden) != 'undefined' && config.hidden){
5268                 c.style += ' display:none;';
5269             }
5270             
5271             if(typeof(config.dataIndex) != 'undefined'){
5272                 c.sort = config.dataIndex;
5273             }
5274             
5275             if(typeof(config.sortable) != 'undefined' && config.sortable){
5276                 c.cls = 'sortable';
5277             }
5278             
5279             if(typeof(config.align) != 'undefined' && config.align.length){
5280                 c.style += ' text-align:' + config.align + ';';
5281             }
5282             
5283             if(typeof(config.width) != 'undefined'){
5284                 c.style += ' width:' + config.width + 'px;';
5285             }
5286             
5287             header.cn.push(c)
5288         }
5289         
5290         return header;
5291     },
5292     
5293     renderBody : function()
5294     {
5295         var body = {
5296             tag: 'tbody',
5297             cn : [
5298                 {
5299                     tag: 'tr',
5300                     cn : [
5301                         {
5302                             tag : 'td',
5303                             colspan :  this.cm.getColumnCount()
5304                         }
5305                     ]
5306                 }
5307             ]
5308         };
5309         
5310         return body;
5311     },
5312     
5313     renderFooter : function()
5314     {
5315         var footer = {
5316             tag: 'tfoot',
5317             cn : [
5318                 {
5319                     tag: 'tr',
5320                     cn : [
5321                         {
5322                             tag : 'td',
5323                             colspan :  this.cm.getColumnCount()
5324                         }
5325                     ]
5326                 }
5327             ]
5328         };
5329         
5330         return footer;
5331     },
5332     
5333     
5334     
5335     onLoad : function()
5336     {
5337         Roo.log('ds onload');
5338         this.clear();
5339         
5340         var _this = this;
5341         var cm = this.cm;
5342         var ds = this.store;
5343         
5344         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5345             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5346             
5347             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5348                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5349             }
5350             
5351             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5352                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5353             }
5354         });
5355         
5356         var tbody =  this.mainBody;
5357               
5358         if(ds.getCount() > 0){
5359             ds.data.each(function(d,rowIndex){
5360                 var row =  this.renderRow(cm, ds, rowIndex);
5361                 
5362                 tbody.createChild(row);
5363                 
5364                 var _this = this;
5365                 
5366                 if(row.cellObjects.length){
5367                     Roo.each(row.cellObjects, function(r){
5368                         _this.renderCellObject(r);
5369                     })
5370                 }
5371                 
5372             }, this);
5373         }
5374         
5375         Roo.each(this.el.select('tbody td', true).elements, function(e){
5376             e.on('mouseover', _this.onMouseover, _this);
5377         });
5378         
5379         Roo.each(this.el.select('tbody td', true).elements, function(e){
5380             e.on('mouseout', _this.onMouseout, _this);
5381         });
5382
5383         //if(this.loadMask){
5384         //    this.maskEl.hide();
5385         //}
5386     },
5387     
5388     
5389     onUpdate : function(ds,record)
5390     {
5391         this.refreshRow(record);
5392     },
5393     onRemove : function(ds, record, index, isUpdate){
5394         if(isUpdate !== true){
5395             this.fireEvent("beforerowremoved", this, index, record);
5396         }
5397         var bt = this.mainBody.dom;
5398         if(bt.rows[index]){
5399             bt.removeChild(bt.rows[index]);
5400         }
5401         
5402         if(isUpdate !== true){
5403             //this.stripeRows(index);
5404             //this.syncRowHeights(index, index);
5405             //this.layout();
5406             this.fireEvent("rowremoved", this, index, record);
5407         }
5408     },
5409     
5410     
5411     refreshRow : function(record){
5412         var ds = this.store, index;
5413         if(typeof record == 'number'){
5414             index = record;
5415             record = ds.getAt(index);
5416         }else{
5417             index = ds.indexOf(record);
5418         }
5419         this.insertRow(ds, index, true);
5420         this.onRemove(ds, record, index+1, true);
5421         //this.syncRowHeights(index, index);
5422         //this.layout();
5423         this.fireEvent("rowupdated", this, index, record);
5424     },
5425     
5426     insertRow : function(dm, rowIndex, isUpdate){
5427         
5428         if(!isUpdate){
5429             this.fireEvent("beforerowsinserted", this, rowIndex);
5430         }
5431             //var s = this.getScrollState();
5432         var row = this.renderRow(this.cm, this.store, rowIndex);
5433         // insert before rowIndex..
5434         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5435         
5436         var _this = this;
5437                 
5438         if(row.cellObjects.length){
5439             Roo.each(row.cellObjects, function(r){
5440                 _this.renderCellObject(r);
5441             })
5442         }
5443             
5444         if(!isUpdate){
5445             this.fireEvent("rowsinserted", this, rowIndex);
5446             //this.syncRowHeights(firstRow, lastRow);
5447             //this.stripeRows(firstRow);
5448             //this.layout();
5449         }
5450         
5451     },
5452     
5453     
5454     getRowDom : function(rowIndex)
5455     {
5456         // not sure if I need to check this.. but let's do it anyway..
5457         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5458                 this.mainBody.dom.rows[rowIndex] : false
5459     },
5460     // returns the object tree for a tr..
5461   
5462     
5463     renderRow : function(cm, ds, rowIndex) {
5464         
5465         var d = ds.getAt(rowIndex);
5466         
5467         var row = {
5468             tag : 'tr',
5469             cn : []
5470         };
5471             
5472         var cellObjects = [];
5473         
5474         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5475             var config = cm.config[i];
5476             
5477             var renderer = cm.getRenderer(i);
5478             var value = '';
5479             var id = false;
5480             
5481             if(typeof(renderer) !== 'undefined'){
5482                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5483             }
5484             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5485             // and are rendered into the cells after the row is rendered - using the id for the element.
5486             
5487             if(typeof(value) === 'object'){
5488                 id = Roo.id();
5489                 cellObjects.push({
5490                     container : id,
5491                     cfg : value 
5492                 })
5493             }
5494             
5495             var rowcfg = {
5496                 record: d,
5497                 rowIndex : rowIndex,
5498                 colIndex : i,
5499                 rowClass : ''
5500             }
5501
5502             this.fireEvent('rowclass', this, rowcfg);
5503             
5504             var td = {
5505                 tag: 'td',
5506                 cls : rowcfg.rowClass,
5507                 style: '',
5508                 html: (typeof(value) === 'object') ? '' : value
5509             };
5510             
5511             if (id) {
5512                 td.id = id;
5513             }
5514             
5515             if(typeof(config.hidden) != 'undefined' && config.hidden){
5516                 td.style += ' display:none;';
5517             }
5518             
5519             if(typeof(config.align) != 'undefined' && config.align.length){
5520                 td.style += ' text-align:' + config.align + ';';
5521             }
5522             
5523             if(typeof(config.width) != 'undefined'){
5524                 td.style += ' width:' +  config.width + 'px;';
5525             }
5526              
5527             row.cn.push(td);
5528            
5529         }
5530         
5531         row.cellObjects = cellObjects;
5532         
5533         return row;
5534           
5535     },
5536     
5537     
5538     
5539     onBeforeLoad : function()
5540     {
5541         //Roo.log('ds onBeforeLoad');
5542         
5543         //this.clear();
5544         
5545         //if(this.loadMask){
5546         //    this.maskEl.show();
5547         //}
5548     },
5549     
5550     clear : function()
5551     {
5552         this.el.select('tbody', true).first().dom.innerHTML = '';
5553     },
5554     
5555     getSelectionModel : function(){
5556         if(!this.selModel){
5557             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5558         }
5559         return this.selModel;
5560     },
5561     /*
5562      * Render the Roo.bootstrap object from renderder
5563      */
5564     renderCellObject : function(r)
5565     {
5566         var _this = this;
5567         
5568         var t = r.cfg.render(r.container);
5569         
5570         if(r.cfg.cn){
5571             Roo.each(r.cfg.cn, function(c){
5572                 var child = {
5573                     container: t.getChildContainer(),
5574                     cfg: c
5575                 }
5576                 _this.renderCellObject(child);
5577             })
5578         }
5579     }
5580    
5581 });
5582
5583  
5584
5585  /*
5586  * - LGPL
5587  *
5588  * table cell
5589  * 
5590  */
5591
5592 /**
5593  * @class Roo.bootstrap.TableCell
5594  * @extends Roo.bootstrap.Component
5595  * Bootstrap TableCell class
5596  * @cfg {String} html cell contain text
5597  * @cfg {String} cls cell class
5598  * @cfg {String} tag cell tag (td|th) default td
5599  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5600  * @cfg {String} align Aligns the content in a cell
5601  * @cfg {String} axis Categorizes cells
5602  * @cfg {String} bgcolor Specifies the background color of a cell
5603  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5604  * @cfg {Number} colspan Specifies the number of columns a cell should span
5605  * @cfg {String} headers Specifies one or more header cells a cell is related to
5606  * @cfg {Number} height Sets the height of a cell
5607  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5608  * @cfg {Number} rowspan Sets the number of rows a cell should span
5609  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5610  * @cfg {String} valign Vertical aligns the content in a cell
5611  * @cfg {Number} width Specifies the width of a cell
5612  * 
5613  * @constructor
5614  * Create a new TableCell
5615  * @param {Object} config The config object
5616  */
5617
5618 Roo.bootstrap.TableCell = function(config){
5619     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5620 };
5621
5622 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5623     
5624     html: false,
5625     cls: false,
5626     tag: false,
5627     abbr: false,
5628     align: false,
5629     axis: false,
5630     bgcolor: false,
5631     charoff: false,
5632     colspan: false,
5633     headers: false,
5634     height: false,
5635     nowrap: false,
5636     rowspan: false,
5637     scope: false,
5638     valign: false,
5639     width: false,
5640     
5641     
5642     getAutoCreate : function(){
5643         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5644         
5645         cfg = {
5646             tag: 'td'
5647         }
5648         
5649         if(this.tag){
5650             cfg.tag = this.tag;
5651         }
5652         
5653         if (this.html) {
5654             cfg.html=this.html
5655         }
5656         if (this.cls) {
5657             cfg.cls=this.cls
5658         }
5659         if (this.abbr) {
5660             cfg.abbr=this.abbr
5661         }
5662         if (this.align) {
5663             cfg.align=this.align
5664         }
5665         if (this.axis) {
5666             cfg.axis=this.axis
5667         }
5668         if (this.bgcolor) {
5669             cfg.bgcolor=this.bgcolor
5670         }
5671         if (this.charoff) {
5672             cfg.charoff=this.charoff
5673         }
5674         if (this.colspan) {
5675             cfg.colspan=this.colspan
5676         }
5677         if (this.headers) {
5678             cfg.headers=this.headers
5679         }
5680         if (this.height) {
5681             cfg.height=this.height
5682         }
5683         if (this.nowrap) {
5684             cfg.nowrap=this.nowrap
5685         }
5686         if (this.rowspan) {
5687             cfg.rowspan=this.rowspan
5688         }
5689         if (this.scope) {
5690             cfg.scope=this.scope
5691         }
5692         if (this.valign) {
5693             cfg.valign=this.valign
5694         }
5695         if (this.width) {
5696             cfg.width=this.width
5697         }
5698         
5699         
5700         return cfg;
5701     }
5702    
5703 });
5704
5705  
5706
5707  /*
5708  * - LGPL
5709  *
5710  * table row
5711  * 
5712  */
5713
5714 /**
5715  * @class Roo.bootstrap.TableRow
5716  * @extends Roo.bootstrap.Component
5717  * Bootstrap TableRow class
5718  * @cfg {String} cls row class
5719  * @cfg {String} align Aligns the content in a table row
5720  * @cfg {String} bgcolor Specifies a background color for a table row
5721  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5722  * @cfg {String} valign Vertical aligns the content in a table row
5723  * 
5724  * @constructor
5725  * Create a new TableRow
5726  * @param {Object} config The config object
5727  */
5728
5729 Roo.bootstrap.TableRow = function(config){
5730     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5731 };
5732
5733 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5734     
5735     cls: false,
5736     align: false,
5737     bgcolor: false,
5738     charoff: false,
5739     valign: false,
5740     
5741     getAutoCreate : function(){
5742         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5743         
5744         cfg = {
5745             tag: 'tr'
5746         }
5747             
5748         if(this.cls){
5749             cfg.cls = this.cls;
5750         }
5751         if(this.align){
5752             cfg.align = this.align;
5753         }
5754         if(this.bgcolor){
5755             cfg.bgcolor = this.bgcolor;
5756         }
5757         if(this.charoff){
5758             cfg.charoff = this.charoff;
5759         }
5760         if(this.valign){
5761             cfg.valign = this.valign;
5762         }
5763         
5764         return cfg;
5765     }
5766    
5767 });
5768
5769  
5770
5771  /*
5772  * - LGPL
5773  *
5774  * table body
5775  * 
5776  */
5777
5778 /**
5779  * @class Roo.bootstrap.TableBody
5780  * @extends Roo.bootstrap.Component
5781  * Bootstrap TableBody class
5782  * @cfg {String} cls element class
5783  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5784  * @cfg {String} align Aligns the content inside the element
5785  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5786  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5787  * 
5788  * @constructor
5789  * Create a new TableBody
5790  * @param {Object} config The config object
5791  */
5792
5793 Roo.bootstrap.TableBody = function(config){
5794     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5795 };
5796
5797 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5798     
5799     cls: false,
5800     tag: false,
5801     align: false,
5802     charoff: false,
5803     valign: false,
5804     
5805     getAutoCreate : function(){
5806         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5807         
5808         cfg = {
5809             tag: 'tbody'
5810         }
5811             
5812         if (this.cls) {
5813             cfg.cls=this.cls
5814         }
5815         if(this.tag){
5816             cfg.tag = this.tag;
5817         }
5818         
5819         if(this.align){
5820             cfg.align = this.align;
5821         }
5822         if(this.charoff){
5823             cfg.charoff = this.charoff;
5824         }
5825         if(this.valign){
5826             cfg.valign = this.valign;
5827         }
5828         
5829         return cfg;
5830     }
5831     
5832     
5833 //    initEvents : function()
5834 //    {
5835 //        
5836 //        if(!this.store){
5837 //            return;
5838 //        }
5839 //        
5840 //        this.store = Roo.factory(this.store, Roo.data);
5841 //        this.store.on('load', this.onLoad, this);
5842 //        
5843 //        this.store.load();
5844 //        
5845 //    },
5846 //    
5847 //    onLoad: function () 
5848 //    {   
5849 //        this.fireEvent('load', this);
5850 //    }
5851 //    
5852 //   
5853 });
5854
5855  
5856
5857  /*
5858  * Based on:
5859  * Ext JS Library 1.1.1
5860  * Copyright(c) 2006-2007, Ext JS, LLC.
5861  *
5862  * Originally Released Under LGPL - original licence link has changed is not relivant.
5863  *
5864  * Fork - LGPL
5865  * <script type="text/javascript">
5866  */
5867
5868 // as we use this in bootstrap.
5869 Roo.namespace('Roo.form');
5870  /**
5871  * @class Roo.form.Action
5872  * Internal Class used to handle form actions
5873  * @constructor
5874  * @param {Roo.form.BasicForm} el The form element or its id
5875  * @param {Object} config Configuration options
5876  */
5877
5878  
5879  
5880 // define the action interface
5881 Roo.form.Action = function(form, options){
5882     this.form = form;
5883     this.options = options || {};
5884 };
5885 /**
5886  * Client Validation Failed
5887  * @const 
5888  */
5889 Roo.form.Action.CLIENT_INVALID = 'client';
5890 /**
5891  * Server Validation Failed
5892  * @const 
5893  */
5894 Roo.form.Action.SERVER_INVALID = 'server';
5895  /**
5896  * Connect to Server Failed
5897  * @const 
5898  */
5899 Roo.form.Action.CONNECT_FAILURE = 'connect';
5900 /**
5901  * Reading Data from Server Failed
5902  * @const 
5903  */
5904 Roo.form.Action.LOAD_FAILURE = 'load';
5905
5906 Roo.form.Action.prototype = {
5907     type : 'default',
5908     failureType : undefined,
5909     response : undefined,
5910     result : undefined,
5911
5912     // interface method
5913     run : function(options){
5914
5915     },
5916
5917     // interface method
5918     success : function(response){
5919
5920     },
5921
5922     // interface method
5923     handleResponse : function(response){
5924
5925     },
5926
5927     // default connection failure
5928     failure : function(response){
5929         
5930         this.response = response;
5931         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5932         this.form.afterAction(this, false);
5933     },
5934
5935     processResponse : function(response){
5936         this.response = response;
5937         if(!response.responseText){
5938             return true;
5939         }
5940         this.result = this.handleResponse(response);
5941         return this.result;
5942     },
5943
5944     // utility functions used internally
5945     getUrl : function(appendParams){
5946         var url = this.options.url || this.form.url || this.form.el.dom.action;
5947         if(appendParams){
5948             var p = this.getParams();
5949             if(p){
5950                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5951             }
5952         }
5953         return url;
5954     },
5955
5956     getMethod : function(){
5957         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5958     },
5959
5960     getParams : function(){
5961         var bp = this.form.baseParams;
5962         var p = this.options.params;
5963         if(p){
5964             if(typeof p == "object"){
5965                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5966             }else if(typeof p == 'string' && bp){
5967                 p += '&' + Roo.urlEncode(bp);
5968             }
5969         }else if(bp){
5970             p = Roo.urlEncode(bp);
5971         }
5972         return p;
5973     },
5974
5975     createCallback : function(){
5976         return {
5977             success: this.success,
5978             failure: this.failure,
5979             scope: this,
5980             timeout: (this.form.timeout*1000),
5981             upload: this.form.fileUpload ? this.success : undefined
5982         };
5983     }
5984 };
5985
5986 Roo.form.Action.Submit = function(form, options){
5987     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5988 };
5989
5990 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5991     type : 'submit',
5992
5993     haveProgress : false,
5994     uploadComplete : false,
5995     
5996     // uploadProgress indicator.
5997     uploadProgress : function()
5998     {
5999         if (!this.form.progressUrl) {
6000             return;
6001         }
6002         
6003         if (!this.haveProgress) {
6004             Roo.MessageBox.progress("Uploading", "Uploading");
6005         }
6006         if (this.uploadComplete) {
6007            Roo.MessageBox.hide();
6008            return;
6009         }
6010         
6011         this.haveProgress = true;
6012    
6013         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6014         
6015         var c = new Roo.data.Connection();
6016         c.request({
6017             url : this.form.progressUrl,
6018             params: {
6019                 id : uid
6020             },
6021             method: 'GET',
6022             success : function(req){
6023                //console.log(data);
6024                 var rdata = false;
6025                 var edata;
6026                 try  {
6027                    rdata = Roo.decode(req.responseText)
6028                 } catch (e) {
6029                     Roo.log("Invalid data from server..");
6030                     Roo.log(edata);
6031                     return;
6032                 }
6033                 if (!rdata || !rdata.success) {
6034                     Roo.log(rdata);
6035                     Roo.MessageBox.alert(Roo.encode(rdata));
6036                     return;
6037                 }
6038                 var data = rdata.data;
6039                 
6040                 if (this.uploadComplete) {
6041                    Roo.MessageBox.hide();
6042                    return;
6043                 }
6044                    
6045                 if (data){
6046                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6047                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6048                     );
6049                 }
6050                 this.uploadProgress.defer(2000,this);
6051             },
6052        
6053             failure: function(data) {
6054                 Roo.log('progress url failed ');
6055                 Roo.log(data);
6056             },
6057             scope : this
6058         });
6059            
6060     },
6061     
6062     
6063     run : function()
6064     {
6065         // run get Values on the form, so it syncs any secondary forms.
6066         this.form.getValues();
6067         
6068         var o = this.options;
6069         var method = this.getMethod();
6070         var isPost = method == 'POST';
6071         if(o.clientValidation === false || this.form.isValid()){
6072             
6073             if (this.form.progressUrl) {
6074                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6075                     (new Date() * 1) + '' + Math.random());
6076                     
6077             } 
6078             
6079             
6080             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6081                 form:this.form.el.dom,
6082                 url:this.getUrl(!isPost),
6083                 method: method,
6084                 params:isPost ? this.getParams() : null,
6085                 isUpload: this.form.fileUpload
6086             }));
6087             
6088             this.uploadProgress();
6089
6090         }else if (o.clientValidation !== false){ // client validation failed
6091             this.failureType = Roo.form.Action.CLIENT_INVALID;
6092             this.form.afterAction(this, false);
6093         }
6094     },
6095
6096     success : function(response)
6097     {
6098         this.uploadComplete= true;
6099         if (this.haveProgress) {
6100             Roo.MessageBox.hide();
6101         }
6102         
6103         
6104         var result = this.processResponse(response);
6105         if(result === true || result.success){
6106             this.form.afterAction(this, true);
6107             return;
6108         }
6109         if(result.errors){
6110             this.form.markInvalid(result.errors);
6111             this.failureType = Roo.form.Action.SERVER_INVALID;
6112         }
6113         this.form.afterAction(this, false);
6114     },
6115     failure : function(response)
6116     {
6117         this.uploadComplete= true;
6118         if (this.haveProgress) {
6119             Roo.MessageBox.hide();
6120         }
6121         
6122         this.response = response;
6123         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6124         this.form.afterAction(this, false);
6125     },
6126     
6127     handleResponse : function(response){
6128         if(this.form.errorReader){
6129             var rs = this.form.errorReader.read(response);
6130             var errors = [];
6131             if(rs.records){
6132                 for(var i = 0, len = rs.records.length; i < len; i++) {
6133                     var r = rs.records[i];
6134                     errors[i] = r.data;
6135                 }
6136             }
6137             if(errors.length < 1){
6138                 errors = null;
6139             }
6140             return {
6141                 success : rs.success,
6142                 errors : errors
6143             };
6144         }
6145         var ret = false;
6146         try {
6147             ret = Roo.decode(response.responseText);
6148         } catch (e) {
6149             ret = {
6150                 success: false,
6151                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6152                 errors : []
6153             };
6154         }
6155         return ret;
6156         
6157     }
6158 });
6159
6160
6161 Roo.form.Action.Load = function(form, options){
6162     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6163     this.reader = this.form.reader;
6164 };
6165
6166 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6167     type : 'load',
6168
6169     run : function(){
6170         
6171         Roo.Ajax.request(Roo.apply(
6172                 this.createCallback(), {
6173                     method:this.getMethod(),
6174                     url:this.getUrl(false),
6175                     params:this.getParams()
6176         }));
6177     },
6178
6179     success : function(response){
6180         
6181         var result = this.processResponse(response);
6182         if(result === true || !result.success || !result.data){
6183             this.failureType = Roo.form.Action.LOAD_FAILURE;
6184             this.form.afterAction(this, false);
6185             return;
6186         }
6187         this.form.clearInvalid();
6188         this.form.setValues(result.data);
6189         this.form.afterAction(this, true);
6190     },
6191
6192     handleResponse : function(response){
6193         if(this.form.reader){
6194             var rs = this.form.reader.read(response);
6195             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6196             return {
6197                 success : rs.success,
6198                 data : data
6199             };
6200         }
6201         return Roo.decode(response.responseText);
6202     }
6203 });
6204
6205 Roo.form.Action.ACTION_TYPES = {
6206     'load' : Roo.form.Action.Load,
6207     'submit' : Roo.form.Action.Submit
6208 };/*
6209  * - LGPL
6210  *
6211  * form
6212  * 
6213  */
6214
6215 /**
6216  * @class Roo.bootstrap.Form
6217  * @extends Roo.bootstrap.Component
6218  * Bootstrap Form class
6219  * @cfg {String} method  GET | POST (default POST)
6220  * @cfg {String} labelAlign top | left (default top)
6221  * @cfg {String} align left  | right - for navbars
6222  * @cfg {Boolean} loadMask load mask when submit (default true)
6223
6224  * 
6225  * @constructor
6226  * Create a new Form
6227  * @param {Object} config The config object
6228  */
6229
6230
6231 Roo.bootstrap.Form = function(config){
6232     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6233     this.addEvents({
6234         /**
6235          * @event clientvalidation
6236          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6237          * @param {Form} this
6238          * @param {Boolean} valid true if the form has passed client-side validation
6239          */
6240         clientvalidation: true,
6241         /**
6242          * @event beforeaction
6243          * Fires before any action is performed. Return false to cancel the action.
6244          * @param {Form} this
6245          * @param {Action} action The action to be performed
6246          */
6247         beforeaction: true,
6248         /**
6249          * @event actionfailed
6250          * Fires when an action fails.
6251          * @param {Form} this
6252          * @param {Action} action The action that failed
6253          */
6254         actionfailed : true,
6255         /**
6256          * @event actioncomplete
6257          * Fires when an action is completed.
6258          * @param {Form} this
6259          * @param {Action} action The action that completed
6260          */
6261         actioncomplete : true
6262     });
6263     
6264 };
6265
6266 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6267       
6268      /**
6269      * @cfg {String} method
6270      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6271      */
6272     method : 'POST',
6273     /**
6274      * @cfg {String} url
6275      * The URL to use for form actions if one isn't supplied in the action options.
6276      */
6277     /**
6278      * @cfg {Boolean} fileUpload
6279      * Set to true if this form is a file upload.
6280      */
6281      
6282     /**
6283      * @cfg {Object} baseParams
6284      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6285      */
6286       
6287     /**
6288      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6289      */
6290     timeout: 30,
6291     /**
6292      * @cfg {Sting} align (left|right) for navbar forms
6293      */
6294     align : 'left',
6295
6296     // private
6297     activeAction : null,
6298  
6299     /**
6300      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6301      * element by passing it or its id or mask the form itself by passing in true.
6302      * @type Mixed
6303      */
6304     waitMsgTarget : false,
6305     
6306     loadMask : true,
6307     
6308     getAutoCreate : function(){
6309         
6310         var cfg = {
6311             tag: 'form',
6312             method : this.method || 'POST',
6313             id : this.id || Roo.id(),
6314             cls : ''
6315         }
6316         if (this.parent().xtype.match(/^Nav/)) {
6317             cfg.cls = 'navbar-form navbar-' + this.align;
6318             
6319         }
6320         
6321         if (this.labelAlign == 'left' ) {
6322             cfg.cls += ' form-horizontal';
6323         }
6324         
6325         
6326         return cfg;
6327     },
6328     initEvents : function()
6329     {
6330         this.el.on('submit', this.onSubmit, this);
6331         // this was added as random key presses on the form where triggering form submit.
6332         this.el.on('keypress', function(e) {
6333             if (e.getCharCode() != 13) {
6334                 return true;
6335             }
6336             // we might need to allow it for textareas.. and some other items.
6337             // check e.getTarget().
6338             
6339             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6340                 return true;
6341             }
6342         
6343             Roo.log("keypress blocked");
6344             
6345             e.preventDefault();
6346             return false;
6347         });
6348         
6349     },
6350     // private
6351     onSubmit : function(e){
6352         e.stopEvent();
6353     },
6354     
6355      /**
6356      * Returns true if client-side validation on the form is successful.
6357      * @return Boolean
6358      */
6359     isValid : function(){
6360         var items = this.getItems();
6361         var valid = true;
6362         items.each(function(f){
6363            if(!f.validate()){
6364                valid = false;
6365                
6366            }
6367         });
6368         return valid;
6369     },
6370     /**
6371      * Returns true if any fields in this form have changed since their original load.
6372      * @return Boolean
6373      */
6374     isDirty : function(){
6375         var dirty = false;
6376         var items = this.getItems();
6377         items.each(function(f){
6378            if(f.isDirty()){
6379                dirty = true;
6380                return false;
6381            }
6382            return true;
6383         });
6384         return dirty;
6385     },
6386      /**
6387      * Performs a predefined action (submit or load) or custom actions you define on this form.
6388      * @param {String} actionName The name of the action type
6389      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6390      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6391      * accept other config options):
6392      * <pre>
6393 Property          Type             Description
6394 ----------------  ---------------  ----------------------------------------------------------------------------------
6395 url               String           The url for the action (defaults to the form's url)
6396 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6397 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6398 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6399                                    validate the form on the client (defaults to false)
6400      * </pre>
6401      * @return {BasicForm} this
6402      */
6403     doAction : function(action, options){
6404         if(typeof action == 'string'){
6405             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6406         }
6407         if(this.fireEvent('beforeaction', this, action) !== false){
6408             this.beforeAction(action);
6409             action.run.defer(100, action);
6410         }
6411         return this;
6412     },
6413     
6414     // private
6415     beforeAction : function(action){
6416         var o = action.options;
6417         
6418         if(this.loadMask){
6419             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6420         }
6421         // not really supported yet.. ??
6422         
6423         //if(this.waitMsgTarget === true){
6424         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6425         //}else if(this.waitMsgTarget){
6426         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6427         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6428         //}else {
6429         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6430        // }
6431          
6432     },
6433
6434     // private
6435     afterAction : function(action, success){
6436         this.activeAction = null;
6437         var o = action.options;
6438         
6439         //if(this.waitMsgTarget === true){
6440             this.el.unmask();
6441         //}else if(this.waitMsgTarget){
6442         //    this.waitMsgTarget.unmask();
6443         //}else{
6444         //    Roo.MessageBox.updateProgress(1);
6445         //    Roo.MessageBox.hide();
6446        // }
6447         // 
6448         if(success){
6449             if(o.reset){
6450                 this.reset();
6451             }
6452             Roo.callback(o.success, o.scope, [this, action]);
6453             this.fireEvent('actioncomplete', this, action);
6454             
6455         }else{
6456             
6457             // failure condition..
6458             // we have a scenario where updates need confirming.
6459             // eg. if a locking scenario exists..
6460             // we look for { errors : { needs_confirm : true }} in the response.
6461             if (
6462                 (typeof(action.result) != 'undefined')  &&
6463                 (typeof(action.result.errors) != 'undefined')  &&
6464                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6465            ){
6466                 var _t = this;
6467                 Roo.log("not supported yet");
6468                  /*
6469                 
6470                 Roo.MessageBox.confirm(
6471                     "Change requires confirmation",
6472                     action.result.errorMsg,
6473                     function(r) {
6474                         if (r != 'yes') {
6475                             return;
6476                         }
6477                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6478                     }
6479                     
6480                 );
6481                 */
6482                 
6483                 
6484                 return;
6485             }
6486             
6487             Roo.callback(o.failure, o.scope, [this, action]);
6488             // show an error message if no failed handler is set..
6489             if (!this.hasListener('actionfailed')) {
6490                 Roo.log("need to add dialog support");
6491                 /*
6492                 Roo.MessageBox.alert("Error",
6493                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6494                         action.result.errorMsg :
6495                         "Saving Failed, please check your entries or try again"
6496                 );
6497                 */
6498             }
6499             
6500             this.fireEvent('actionfailed', this, action);
6501         }
6502         
6503     },
6504     /**
6505      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6506      * @param {String} id The value to search for
6507      * @return Field
6508      */
6509     findField : function(id){
6510         var items = this.getItems();
6511         var field = items.get(id);
6512         if(!field){
6513              items.each(function(f){
6514                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6515                     field = f;
6516                     return false;
6517                 }
6518                 return true;
6519             });
6520         }
6521         return field || null;
6522     },
6523      /**
6524      * Mark fields in this form invalid in bulk.
6525      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6526      * @return {BasicForm} this
6527      */
6528     markInvalid : function(errors){
6529         if(errors instanceof Array){
6530             for(var i = 0, len = errors.length; i < len; i++){
6531                 var fieldError = errors[i];
6532                 var f = this.findField(fieldError.id);
6533                 if(f){
6534                     f.markInvalid(fieldError.msg);
6535                 }
6536             }
6537         }else{
6538             var field, id;
6539             for(id in errors){
6540                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6541                     field.markInvalid(errors[id]);
6542                 }
6543             }
6544         }
6545         //Roo.each(this.childForms || [], function (f) {
6546         //    f.markInvalid(errors);
6547         //});
6548         
6549         return this;
6550     },
6551
6552     /**
6553      * Set values for fields in this form in bulk.
6554      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6555      * @return {BasicForm} this
6556      */
6557     setValues : function(values){
6558         if(values instanceof Array){ // array of objects
6559             for(var i = 0, len = values.length; i < len; i++){
6560                 var v = values[i];
6561                 var f = this.findField(v.id);
6562                 if(f){
6563                     f.setValue(v.value);
6564                     if(this.trackResetOnLoad){
6565                         f.originalValue = f.getValue();
6566                     }
6567                 }
6568             }
6569         }else{ // object hash
6570             var field, id;
6571             for(id in values){
6572                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6573                     
6574                     if (field.setFromData && 
6575                         field.valueField && 
6576                         field.displayField &&
6577                         // combos' with local stores can 
6578                         // be queried via setValue()
6579                         // to set their value..
6580                         (field.store && !field.store.isLocal)
6581                         ) {
6582                         // it's a combo
6583                         var sd = { };
6584                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6585                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6586                         field.setFromData(sd);
6587                         
6588                     } else {
6589                         field.setValue(values[id]);
6590                     }
6591                     
6592                     
6593                     if(this.trackResetOnLoad){
6594                         field.originalValue = field.getValue();
6595                     }
6596                 }
6597             }
6598         }
6599          
6600         //Roo.each(this.childForms || [], function (f) {
6601         //    f.setValues(values);
6602         //});
6603                 
6604         return this;
6605     },
6606
6607     /**
6608      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6609      * they are returned as an array.
6610      * @param {Boolean} asString
6611      * @return {Object}
6612      */
6613     getValues : function(asString){
6614         //if (this.childForms) {
6615             // copy values from the child forms
6616         //    Roo.each(this.childForms, function (f) {
6617         //        this.setValues(f.getValues());
6618         //    }, this);
6619         //}
6620         
6621         
6622         
6623         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6624         if(asString === true){
6625             return fs;
6626         }
6627         return Roo.urlDecode(fs);
6628     },
6629     
6630     /**
6631      * Returns the fields in this form as an object with key/value pairs. 
6632      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6633      * @return {Object}
6634      */
6635     getFieldValues : function(with_hidden)
6636     {
6637         var items = this.getItems();
6638         var ret = {};
6639         items.each(function(f){
6640             if (!f.getName()) {
6641                 return;
6642             }
6643             var v = f.getValue();
6644             if (f.inputType =='radio') {
6645                 if (typeof(ret[f.getName()]) == 'undefined') {
6646                     ret[f.getName()] = ''; // empty..
6647                 }
6648                 
6649                 if (!f.el.dom.checked) {
6650                     return;
6651                     
6652                 }
6653                 v = f.el.dom.value;
6654                 
6655             }
6656             
6657             // not sure if this supported any more..
6658             if ((typeof(v) == 'object') && f.getRawValue) {
6659                 v = f.getRawValue() ; // dates..
6660             }
6661             // combo boxes where name != hiddenName...
6662             if (f.name != f.getName()) {
6663                 ret[f.name] = f.getRawValue();
6664             }
6665             ret[f.getName()] = v;
6666         });
6667         
6668         return ret;
6669     },
6670
6671     /**
6672      * Clears all invalid messages in this form.
6673      * @return {BasicForm} this
6674      */
6675     clearInvalid : function(){
6676         var items = this.getItems();
6677         
6678         items.each(function(f){
6679            f.clearInvalid();
6680         });
6681         
6682         
6683         
6684         return this;
6685     },
6686
6687     /**
6688      * Resets this form.
6689      * @return {BasicForm} this
6690      */
6691     reset : function(){
6692         var items = this.getItems();
6693         items.each(function(f){
6694             f.reset();
6695         });
6696         
6697         Roo.each(this.childForms || [], function (f) {
6698             f.reset();
6699         });
6700        
6701         
6702         return this;
6703     },
6704     getItems : function()
6705     {
6706         var r=new Roo.util.MixedCollection(false, function(o){
6707             return o.id || (o.id = Roo.id());
6708         });
6709         var iter = function(el) {
6710             if (el.inputEl) {
6711                 r.add(el);
6712             }
6713             if (!el.items) {
6714                 return;
6715             }
6716             Roo.each(el.items,function(e) {
6717                 iter(e);
6718             });
6719             
6720             
6721         };
6722         iter(this);
6723         return r;
6724         
6725         
6726         
6727         
6728     }
6729     
6730 });
6731
6732  
6733 /*
6734  * Based on:
6735  * Ext JS Library 1.1.1
6736  * Copyright(c) 2006-2007, Ext JS, LLC.
6737  *
6738  * Originally Released Under LGPL - original licence link has changed is not relivant.
6739  *
6740  * Fork - LGPL
6741  * <script type="text/javascript">
6742  */
6743 /**
6744  * @class Roo.form.VTypes
6745  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6746  * @singleton
6747  */
6748 Roo.form.VTypes = function(){
6749     // closure these in so they are only created once.
6750     var alpha = /^[a-zA-Z_]+$/;
6751     var alphanum = /^[a-zA-Z0-9_]+$/;
6752     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6753     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6754
6755     // All these messages and functions are configurable
6756     return {
6757         /**
6758          * The function used to validate email addresses
6759          * @param {String} value The email address
6760          */
6761         'email' : function(v){
6762             return email.test(v);
6763         },
6764         /**
6765          * The error text to display when the email validation function returns false
6766          * @type String
6767          */
6768         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6769         /**
6770          * The keystroke filter mask to be applied on email input
6771          * @type RegExp
6772          */
6773         'emailMask' : /[a-z0-9_\.\-@]/i,
6774
6775         /**
6776          * The function used to validate URLs
6777          * @param {String} value The URL
6778          */
6779         'url' : function(v){
6780             return url.test(v);
6781         },
6782         /**
6783          * The error text to display when the url validation function returns false
6784          * @type String
6785          */
6786         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6787         
6788         /**
6789          * The function used to validate alpha values
6790          * @param {String} value The value
6791          */
6792         'alpha' : function(v){
6793             return alpha.test(v);
6794         },
6795         /**
6796          * The error text to display when the alpha validation function returns false
6797          * @type String
6798          */
6799         'alphaText' : 'This field should only contain letters and _',
6800         /**
6801          * The keystroke filter mask to be applied on alpha input
6802          * @type RegExp
6803          */
6804         'alphaMask' : /[a-z_]/i,
6805
6806         /**
6807          * The function used to validate alphanumeric values
6808          * @param {String} value The value
6809          */
6810         'alphanum' : function(v){
6811             return alphanum.test(v);
6812         },
6813         /**
6814          * The error text to display when the alphanumeric validation function returns false
6815          * @type String
6816          */
6817         'alphanumText' : 'This field should only contain letters, numbers and _',
6818         /**
6819          * The keystroke filter mask to be applied on alphanumeric input
6820          * @type RegExp
6821          */
6822         'alphanumMask' : /[a-z0-9_]/i
6823     };
6824 }();/*
6825  * - LGPL
6826  *
6827  * Input
6828  * 
6829  */
6830
6831 /**
6832  * @class Roo.bootstrap.Input
6833  * @extends Roo.bootstrap.Component
6834  * Bootstrap Input class
6835  * @cfg {Boolean} disabled is it disabled
6836  * @cfg {String} fieldLabel - the label associated
6837  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6838  * @cfg {String} name name of the input
6839  * @cfg {string} fieldLabel - the label associated
6840  * @cfg {string}  inputType - input / file submit ...
6841  * @cfg {string} placeholder - placeholder to put in text.
6842  * @cfg {string}  before - input group add on before
6843  * @cfg {string} after - input group add on after
6844  * @cfg {string} size - (lg|sm) or leave empty..
6845  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6846  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6847  * @cfg {Number} md colspan out of 12 for computer-sized screens
6848  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6849  * @cfg {string} value default value of the input
6850  * @cfg {Number} labelWidth set the width of label (0-12)
6851  * @cfg {String} labelAlign (top|left)
6852  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6853  * @cfg {String} align (left|center|right) Default left
6854  * 
6855  * 
6856  * @constructor
6857  * Create a new Input
6858  * @param {Object} config The config object
6859  */
6860
6861 Roo.bootstrap.Input = function(config){
6862     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6863    
6864         this.addEvents({
6865             /**
6866              * @event focus
6867              * Fires when this field receives input focus.
6868              * @param {Roo.form.Field} this
6869              */
6870             focus : true,
6871             /**
6872              * @event blur
6873              * Fires when this field loses input focus.
6874              * @param {Roo.form.Field} this
6875              */
6876             blur : true,
6877             /**
6878              * @event specialkey
6879              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6880              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6881              * @param {Roo.form.Field} this
6882              * @param {Roo.EventObject} e The event object
6883              */
6884             specialkey : true,
6885             /**
6886              * @event change
6887              * Fires just before the field blurs if the field value has changed.
6888              * @param {Roo.form.Field} this
6889              * @param {Mixed} newValue The new value
6890              * @param {Mixed} oldValue The original value
6891              */
6892             change : true,
6893             /**
6894              * @event invalid
6895              * Fires after the field has been marked as invalid.
6896              * @param {Roo.form.Field} this
6897              * @param {String} msg The validation message
6898              */
6899             invalid : true,
6900             /**
6901              * @event valid
6902              * Fires after the field has been validated with no errors.
6903              * @param {Roo.form.Field} this
6904              */
6905             valid : true,
6906              /**
6907              * @event keyup
6908              * Fires after the key up
6909              * @param {Roo.form.Field} this
6910              * @param {Roo.EventObject}  e The event Object
6911              */
6912             keyup : true
6913         });
6914 };
6915
6916 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6917      /**
6918      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6919       automatic validation (defaults to "keyup").
6920      */
6921     validationEvent : "keyup",
6922      /**
6923      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6924      */
6925     validateOnBlur : true,
6926     /**
6927      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6928      */
6929     validationDelay : 250,
6930      /**
6931      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6932      */
6933     focusClass : "x-form-focus",  // not needed???
6934     
6935        
6936     /**
6937      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6938      */
6939     invalidClass : "has-error",
6940     
6941     /**
6942      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6943      */
6944     selectOnFocus : false,
6945     
6946      /**
6947      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6948      */
6949     maskRe : null,
6950        /**
6951      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6952      */
6953     vtype : null,
6954     
6955       /**
6956      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6957      */
6958     disableKeyFilter : false,
6959     
6960        /**
6961      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6962      */
6963     disabled : false,
6964      /**
6965      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6966      */
6967     allowBlank : true,
6968     /**
6969      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6970      */
6971     blankText : "This field is required",
6972     
6973      /**
6974      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6975      */
6976     minLength : 0,
6977     /**
6978      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6979      */
6980     maxLength : Number.MAX_VALUE,
6981     /**
6982      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6983      */
6984     minLengthText : "The minimum length for this field is {0}",
6985     /**
6986      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6987      */
6988     maxLengthText : "The maximum length for this field is {0}",
6989   
6990     
6991     /**
6992      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6993      * If available, this function will be called only after the basic validators all return true, and will be passed the
6994      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6995      */
6996     validator : null,
6997     /**
6998      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6999      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7000      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7001      */
7002     regex : null,
7003     /**
7004      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7005      */
7006     regexText : "",
7007     
7008     
7009     
7010     fieldLabel : '',
7011     inputType : 'text',
7012     
7013     name : false,
7014     placeholder: false,
7015     before : false,
7016     after : false,
7017     size : false,
7018     // private
7019     hasFocus : false,
7020     preventMark: false,
7021     isFormField : true,
7022     value : '',
7023     labelWidth : 2,
7024     labelAlign : false,
7025     readOnly : false,
7026     align : false,
7027     formatedValue : false,
7028     
7029     parentLabelAlign : function()
7030     {
7031         var parent = this;
7032         while (parent.parent()) {
7033             parent = parent.parent();
7034             if (typeof(parent.labelAlign) !='undefined') {
7035                 return parent.labelAlign;
7036             }
7037         }
7038         return 'left';
7039         
7040     },
7041     
7042     getAutoCreate : function(){
7043         
7044         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7045         
7046         var id = Roo.id();
7047         
7048         var cfg = {};
7049         
7050         if(this.inputType != 'hidden'){
7051             cfg.cls = 'form-group' //input-group
7052         }
7053         
7054         var input =  {
7055             tag: 'input',
7056             id : id,
7057             type : this.inputType,
7058             value : this.value,
7059             cls : 'form-control',
7060             placeholder : this.placeholder || ''
7061             
7062         };
7063         
7064         if(this.align){
7065             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7066         }
7067         
7068         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7069             input.maxLength = this.maxLength;
7070         }
7071         
7072         if (this.disabled) {
7073             input.disabled=true;
7074         }
7075         
7076         if (this.readOnly) {
7077             input.readonly=true;
7078         }
7079         
7080         if (this.name) {
7081             input.name = this.name;
7082         }
7083         if (this.size) {
7084             input.cls += ' input-' + this.size;
7085         }
7086         var settings=this;
7087         ['xs','sm','md','lg'].map(function(size){
7088             if (settings[size]) {
7089                 cfg.cls += ' col-' + size + '-' + settings[size];
7090             }
7091         });
7092         
7093         var inputblock = input;
7094         
7095         if (this.before || this.after) {
7096             
7097             inputblock = {
7098                 cls : 'input-group',
7099                 cn :  [] 
7100             };
7101             if (this.before && typeof(this.before) == 'string') {
7102                 
7103                 inputblock.cn.push({
7104                     tag :'span',
7105                     cls : 'roo-input-before input-group-addon',
7106                     html : this.before
7107                 });
7108             }
7109             if (this.before && typeof(this.before) == 'object') {
7110                 this.before = Roo.factory(this.before);
7111                 Roo.log(this.before);
7112                 inputblock.cn.push({
7113                     tag :'span',
7114                     cls : 'roo-input-before input-group-' +
7115                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7116                 });
7117             }
7118             
7119             inputblock.cn.push(input);
7120             
7121             if (this.after && typeof(this.after) == 'string') {
7122                 inputblock.cn.push({
7123                     tag :'span',
7124                     cls : 'roo-input-after input-group-addon',
7125                     html : this.after
7126                 });
7127             }
7128             if (this.after && typeof(this.after) == 'object') {
7129                 this.after = Roo.factory(this.after);
7130                 Roo.log(this.after);
7131                 inputblock.cn.push({
7132                     tag :'span',
7133                     cls : 'roo-input-after input-group-' +
7134                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7135                 });
7136             }
7137         };
7138         
7139         if (align ==='left' && this.fieldLabel.length) {
7140                 Roo.log("left and has label");
7141                 cfg.cn = [
7142                     
7143                     {
7144                         tag: 'label',
7145                         'for' :  id,
7146                         cls : 'control-label col-sm-' + this.labelWidth,
7147                         html : this.fieldLabel
7148                         
7149                     },
7150                     {
7151                         cls : "col-sm-" + (12 - this.labelWidth), 
7152                         cn: [
7153                             inputblock
7154                         ]
7155                     }
7156                     
7157                 ];
7158         } else if ( this.fieldLabel.length) {
7159                 Roo.log(" label");
7160                  cfg.cn = [
7161                    
7162                     {
7163                         tag: 'label',
7164                         //cls : 'input-group-addon',
7165                         html : this.fieldLabel
7166                         
7167                     },
7168                     
7169                     inputblock
7170                     
7171                 ];
7172
7173         } else {
7174             
7175                 Roo.log(" no label && no align");
7176                 cfg.cn = [
7177                     
7178                         inputblock
7179                     
7180                 ];
7181                 
7182                 
7183         };
7184         Roo.log('input-parentType: ' + this.parentType);
7185         
7186         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7187            cfg.cls += ' navbar-form';
7188            Roo.log(cfg);
7189         }
7190         
7191         return cfg;
7192         
7193     },
7194     /**
7195      * return the real input element.
7196      */
7197     inputEl: function ()
7198     {
7199         return this.el.select('input.form-control',true).first();
7200     },
7201     setDisabled : function(v)
7202     {
7203         var i  = this.inputEl().dom;
7204         if (!v) {
7205             i.removeAttribute('disabled');
7206             return;
7207             
7208         }
7209         i.setAttribute('disabled','true');
7210     },
7211     initEvents : function()
7212     {
7213         
7214         this.inputEl().on("keydown" , this.fireKey,  this);
7215         this.inputEl().on("focus", this.onFocus,  this);
7216         this.inputEl().on("blur", this.onBlur,  this);
7217         
7218         this.inputEl().relayEvent('keyup', this);
7219
7220         // reference to original value for reset
7221         this.originalValue = this.getValue();
7222         //Roo.form.TextField.superclass.initEvents.call(this);
7223         if(this.validationEvent == 'keyup'){
7224             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7225             this.inputEl().on('keyup', this.filterValidation, this);
7226         }
7227         else if(this.validationEvent !== false){
7228             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7229         }
7230         
7231         if(this.selectOnFocus){
7232             this.on("focus", this.preFocus, this);
7233             
7234         }
7235         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7236             this.inputEl().on("keypress", this.filterKeys, this);
7237         }
7238        /* if(this.grow){
7239             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7240             this.el.on("click", this.autoSize,  this);
7241         }
7242         */
7243         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7244             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7245         }
7246         
7247         if (typeof(this.before) == 'object') {
7248             this.before.render(this.el.select('.roo-input-before',true).first());
7249         }
7250         if (typeof(this.after) == 'object') {
7251             this.after.render(this.el.select('.roo-input-after',true).first());
7252         }
7253         
7254         
7255     },
7256     filterValidation : function(e){
7257         if(!e.isNavKeyPress()){
7258             this.validationTask.delay(this.validationDelay);
7259         }
7260     },
7261      /**
7262      * Validates the field value
7263      * @return {Boolean} True if the value is valid, else false
7264      */
7265     validate : function(){
7266         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7267         if(this.disabled || this.validateValue(this.getRawValue())){
7268             this.clearInvalid();
7269             return true;
7270         }
7271         return false;
7272     },
7273     
7274     
7275     /**
7276      * Validates a value according to the field's validation rules and marks the field as invalid
7277      * if the validation fails
7278      * @param {Mixed} value The value to validate
7279      * @return {Boolean} True if the value is valid, else false
7280      */
7281     validateValue : function(value){
7282         if(value.length < 1)  { // if it's blank
7283              if(this.allowBlank){
7284                 this.clearInvalid();
7285                 return true;
7286              }else{
7287                 this.markInvalid(this.blankText);
7288                 return false;
7289              }
7290         }
7291         if(value.length < this.minLength){
7292             this.markInvalid(String.format(this.minLengthText, this.minLength));
7293             return false;
7294         }
7295         if(value.length > this.maxLength){
7296             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7297             return false;
7298         }
7299         if(this.vtype){
7300             var vt = Roo.form.VTypes;
7301             if(!vt[this.vtype](value, this)){
7302                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7303                 return false;
7304             }
7305         }
7306         if(typeof this.validator == "function"){
7307             var msg = this.validator(value);
7308             if(msg !== true){
7309                 this.markInvalid(msg);
7310                 return false;
7311             }
7312         }
7313         if(this.regex && !this.regex.test(value)){
7314             this.markInvalid(this.regexText);
7315             return false;
7316         }
7317         return true;
7318     },
7319
7320     
7321     
7322      // private
7323     fireKey : function(e){
7324         //Roo.log('field ' + e.getKey());
7325         if(e.isNavKeyPress()){
7326             this.fireEvent("specialkey", this, e);
7327         }
7328     },
7329     focus : function (selectText){
7330         if(this.rendered){
7331             this.inputEl().focus();
7332             if(selectText === true){
7333                 this.inputEl().dom.select();
7334             }
7335         }
7336         return this;
7337     } ,
7338     
7339     onFocus : function(){
7340         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7341            // this.el.addClass(this.focusClass);
7342         }
7343         if(!this.hasFocus){
7344             this.hasFocus = true;
7345             this.startValue = this.getValue();
7346             this.fireEvent("focus", this);
7347         }
7348     },
7349     
7350     beforeBlur : Roo.emptyFn,
7351
7352     
7353     // private
7354     onBlur : function(){
7355         this.beforeBlur();
7356         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7357             //this.el.removeClass(this.focusClass);
7358         }
7359         this.hasFocus = false;
7360         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7361             this.validate();
7362         }
7363         var v = this.getValue();
7364         if(String(v) !== String(this.startValue)){
7365             this.fireEvent('change', this, v, this.startValue);
7366         }
7367         this.fireEvent("blur", this);
7368     },
7369     
7370     /**
7371      * Resets the current field value to the originally loaded value and clears any validation messages
7372      */
7373     reset : function(){
7374         this.setValue(this.originalValue);
7375         this.clearInvalid();
7376     },
7377      /**
7378      * Returns the name of the field
7379      * @return {Mixed} name The name field
7380      */
7381     getName: function(){
7382         return this.name;
7383     },
7384      /**
7385      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7386      * @return {Mixed} value The field value
7387      */
7388     getValue : function(){
7389         
7390         var v = this.inputEl().getValue();
7391         
7392         return v;
7393     },
7394     /**
7395      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7396      * @return {Mixed} value The field value
7397      */
7398     getRawValue : function(){
7399         var v = this.inputEl().getValue();
7400         
7401         return v;
7402     },
7403     
7404     /**
7405      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7406      * @param {Mixed} value The value to set
7407      */
7408     setRawValue : function(v){
7409         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7410     },
7411     
7412     selectText : function(start, end){
7413         var v = this.getRawValue();
7414         if(v.length > 0){
7415             start = start === undefined ? 0 : start;
7416             end = end === undefined ? v.length : end;
7417             var d = this.inputEl().dom;
7418             if(d.setSelectionRange){
7419                 d.setSelectionRange(start, end);
7420             }else if(d.createTextRange){
7421                 var range = d.createTextRange();
7422                 range.moveStart("character", start);
7423                 range.moveEnd("character", v.length-end);
7424                 range.select();
7425             }
7426         }
7427     },
7428     
7429     /**
7430      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7431      * @param {Mixed} value The value to set
7432      */
7433     setValue : function(v){
7434         this.value = v;
7435         if(this.rendered){
7436             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7437             this.validate();
7438         }
7439     },
7440     
7441     /*
7442     processValue : function(value){
7443         if(this.stripCharsRe){
7444             var newValue = value.replace(this.stripCharsRe, '');
7445             if(newValue !== value){
7446                 this.setRawValue(newValue);
7447                 return newValue;
7448             }
7449         }
7450         return value;
7451     },
7452   */
7453     preFocus : function(){
7454         
7455         if(this.selectOnFocus){
7456             this.inputEl().dom.select();
7457         }
7458     },
7459     filterKeys : function(e){
7460         var k = e.getKey();
7461         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7462             return;
7463         }
7464         var c = e.getCharCode(), cc = String.fromCharCode(c);
7465         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7466             return;
7467         }
7468         if(!this.maskRe.test(cc)){
7469             e.stopEvent();
7470         }
7471     },
7472      /**
7473      * Clear any invalid styles/messages for this field
7474      */
7475     clearInvalid : function(){
7476         
7477         if(!this.el || this.preventMark){ // not rendered
7478             return;
7479         }
7480         this.el.removeClass(this.invalidClass);
7481         /*
7482         switch(this.msgTarget){
7483             case 'qtip':
7484                 this.el.dom.qtip = '';
7485                 break;
7486             case 'title':
7487                 this.el.dom.title = '';
7488                 break;
7489             case 'under':
7490                 if(this.errorEl){
7491                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7492                 }
7493                 break;
7494             case 'side':
7495                 if(this.errorIcon){
7496                     this.errorIcon.dom.qtip = '';
7497                     this.errorIcon.hide();
7498                     this.un('resize', this.alignErrorIcon, this);
7499                 }
7500                 break;
7501             default:
7502                 var t = Roo.getDom(this.msgTarget);
7503                 t.innerHTML = '';
7504                 t.style.display = 'none';
7505                 break;
7506         }
7507         */
7508         this.fireEvent('valid', this);
7509     },
7510      /**
7511      * Mark this field as invalid
7512      * @param {String} msg The validation message
7513      */
7514     markInvalid : function(msg){
7515         if(!this.el  || this.preventMark){ // not rendered
7516             return;
7517         }
7518         this.el.addClass(this.invalidClass);
7519         /*
7520         msg = msg || this.invalidText;
7521         switch(this.msgTarget){
7522             case 'qtip':
7523                 this.el.dom.qtip = msg;
7524                 this.el.dom.qclass = 'x-form-invalid-tip';
7525                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7526                     Roo.QuickTips.enable();
7527                 }
7528                 break;
7529             case 'title':
7530                 this.el.dom.title = msg;
7531                 break;
7532             case 'under':
7533                 if(!this.errorEl){
7534                     var elp = this.el.findParent('.x-form-element', 5, true);
7535                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7536                     this.errorEl.setWidth(elp.getWidth(true)-20);
7537                 }
7538                 this.errorEl.update(msg);
7539                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7540                 break;
7541             case 'side':
7542                 if(!this.errorIcon){
7543                     var elp = this.el.findParent('.x-form-element', 5, true);
7544                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7545                 }
7546                 this.alignErrorIcon();
7547                 this.errorIcon.dom.qtip = msg;
7548                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7549                 this.errorIcon.show();
7550                 this.on('resize', this.alignErrorIcon, this);
7551                 break;
7552             default:
7553                 var t = Roo.getDom(this.msgTarget);
7554                 t.innerHTML = msg;
7555                 t.style.display = this.msgDisplay;
7556                 break;
7557         }
7558         */
7559         this.fireEvent('invalid', this, msg);
7560     },
7561     // private
7562     SafariOnKeyDown : function(event)
7563     {
7564         // this is a workaround for a password hang bug on chrome/ webkit.
7565         
7566         var isSelectAll = false;
7567         
7568         if(this.inputEl().dom.selectionEnd > 0){
7569             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7570         }
7571         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7572             event.preventDefault();
7573             this.setValue('');
7574             return;
7575         }
7576         
7577         if(isSelectAll){ // backspace and delete key
7578             
7579             event.preventDefault();
7580             // this is very hacky as keydown always get's upper case.
7581             //
7582             var cc = String.fromCharCode(event.getCharCode());
7583             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7584             
7585         }
7586     },
7587     adjustWidth : function(tag, w){
7588         tag = tag.toLowerCase();
7589         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7590             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7591                 if(tag == 'input'){
7592                     return w + 2;
7593                 }
7594                 if(tag == 'textarea'){
7595                     return w-2;
7596                 }
7597             }else if(Roo.isOpera){
7598                 if(tag == 'input'){
7599                     return w + 2;
7600                 }
7601                 if(tag == 'textarea'){
7602                     return w-2;
7603                 }
7604             }
7605         }
7606         return w;
7607     }
7608     
7609 });
7610
7611  
7612 /*
7613  * - LGPL
7614  *
7615  * Input
7616  * 
7617  */
7618
7619 /**
7620  * @class Roo.bootstrap.TextArea
7621  * @extends Roo.bootstrap.Input
7622  * Bootstrap TextArea class
7623  * @cfg {Number} cols Specifies the visible width of a text area
7624  * @cfg {Number} rows Specifies the visible number of lines in a text area
7625  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7626  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7627  * @cfg {string} html text
7628  * 
7629  * @constructor
7630  * Create a new TextArea
7631  * @param {Object} config The config object
7632  */
7633
7634 Roo.bootstrap.TextArea = function(config){
7635     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7636    
7637 };
7638
7639 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7640      
7641     cols : false,
7642     rows : 5,
7643     readOnly : false,
7644     warp : 'soft',
7645     resize : false,
7646     value: false,
7647     html: false,
7648     
7649     getAutoCreate : function(){
7650         
7651         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7652         
7653         var id = Roo.id();
7654         
7655         var cfg = {};
7656         
7657         var input =  {
7658             tag: 'textarea',
7659             id : id,
7660             warp : this.warp,
7661             rows : this.rows,
7662             value : this.value || '',
7663             html: this.html || '',
7664             cls : 'form-control',
7665             placeholder : this.placeholder || '' 
7666             
7667         };
7668         
7669         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7670             input.maxLength = this.maxLength;
7671         }
7672         
7673         if(this.resize){
7674             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7675         }
7676         
7677         if(this.cols){
7678             input.cols = this.cols;
7679         }
7680         
7681         if (this.readOnly) {
7682             input.readonly = true;
7683         }
7684         
7685         if (this.name) {
7686             input.name = this.name;
7687         }
7688         
7689         if (this.size) {
7690             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7691         }
7692         
7693         var settings=this;
7694         ['xs','sm','md','lg'].map(function(size){
7695             if (settings[size]) {
7696                 cfg.cls += ' col-' + size + '-' + settings[size];
7697             }
7698         });
7699         
7700         var inputblock = input;
7701         
7702         if (this.before || this.after) {
7703             
7704             inputblock = {
7705                 cls : 'input-group',
7706                 cn :  [] 
7707             };
7708             if (this.before) {
7709                 inputblock.cn.push({
7710                     tag :'span',
7711                     cls : 'input-group-addon',
7712                     html : this.before
7713                 });
7714             }
7715             inputblock.cn.push(input);
7716             if (this.after) {
7717                 inputblock.cn.push({
7718                     tag :'span',
7719                     cls : 'input-group-addon',
7720                     html : this.after
7721                 });
7722             }
7723             
7724         }
7725         
7726         if (align ==='left' && this.fieldLabel.length) {
7727                 Roo.log("left and has label");
7728                 cfg.cn = [
7729                     
7730                     {
7731                         tag: 'label',
7732                         'for' :  id,
7733                         cls : 'control-label col-sm-' + this.labelWidth,
7734                         html : this.fieldLabel
7735                         
7736                     },
7737                     {
7738                         cls : "col-sm-" + (12 - this.labelWidth), 
7739                         cn: [
7740                             inputblock
7741                         ]
7742                     }
7743                     
7744                 ];
7745         } else if ( this.fieldLabel.length) {
7746                 Roo.log(" label");
7747                  cfg.cn = [
7748                    
7749                     {
7750                         tag: 'label',
7751                         //cls : 'input-group-addon',
7752                         html : this.fieldLabel
7753                         
7754                     },
7755                     
7756                     inputblock
7757                     
7758                 ];
7759
7760         } else {
7761             
7762                    Roo.log(" no label && no align");
7763                 cfg.cn = [
7764                     
7765                         inputblock
7766                     
7767                 ];
7768                 
7769                 
7770         }
7771         
7772         if (this.disabled) {
7773             input.disabled=true;
7774         }
7775         
7776         return cfg;
7777         
7778     },
7779     /**
7780      * return the real textarea element.
7781      */
7782     inputEl: function ()
7783     {
7784         return this.el.select('textarea.form-control',true).first();
7785     }
7786 });
7787
7788  
7789 /*
7790  * - LGPL
7791  *
7792  * trigger field - base class for combo..
7793  * 
7794  */
7795  
7796 /**
7797  * @class Roo.bootstrap.TriggerField
7798  * @extends Roo.bootstrap.Input
7799  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7800  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7801  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7802  * for which you can provide a custom implementation.  For example:
7803  * <pre><code>
7804 var trigger = new Roo.bootstrap.TriggerField();
7805 trigger.onTriggerClick = myTriggerFn;
7806 trigger.applyTo('my-field');
7807 </code></pre>
7808  *
7809  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7810  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7811  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7812  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7813  * @constructor
7814  * Create a new TriggerField.
7815  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7816  * to the base TextField)
7817  */
7818 Roo.bootstrap.TriggerField = function(config){
7819     this.mimicing = false;
7820     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7821 };
7822
7823 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7824     /**
7825      * @cfg {String} triggerClass A CSS class to apply to the trigger
7826      */
7827      /**
7828      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7829      */
7830     hideTrigger:false,
7831
7832     /** @cfg {Boolean} grow @hide */
7833     /** @cfg {Number} growMin @hide */
7834     /** @cfg {Number} growMax @hide */
7835
7836     /**
7837      * @hide 
7838      * @method
7839      */
7840     autoSize: Roo.emptyFn,
7841     // private
7842     monitorTab : true,
7843     // private
7844     deferHeight : true,
7845
7846     
7847     actionMode : 'wrap',
7848     
7849     
7850     
7851     getAutoCreate : function(){
7852        
7853         var align = this.labelAlign || this.parentLabelAlign();
7854         
7855         var id = Roo.id();
7856         
7857         var cfg = {
7858             cls: 'form-group' //input-group
7859         };
7860         
7861         
7862         var input =  {
7863             tag: 'input',
7864             id : id,
7865             type : this.inputType,
7866             cls : 'form-control',
7867             autocomplete: 'off',
7868             placeholder : this.placeholder || '' 
7869             
7870         };
7871         if (this.name) {
7872             input.name = this.name;
7873         }
7874         if (this.size) {
7875             input.cls += ' input-' + this.size;
7876         }
7877         
7878         if (this.disabled) {
7879             input.disabled=true;
7880         }
7881         
7882         var inputblock = input;
7883         
7884         if (this.before || this.after) {
7885             
7886             inputblock = {
7887                 cls : 'input-group',
7888                 cn :  [] 
7889             };
7890             if (this.before) {
7891                 inputblock.cn.push({
7892                     tag :'span',
7893                     cls : 'input-group-addon',
7894                     html : this.before
7895                 });
7896             }
7897             inputblock.cn.push(input);
7898             if (this.after) {
7899                 inputblock.cn.push({
7900                     tag :'span',
7901                     cls : 'input-group-addon',
7902                     html : this.after
7903                 });
7904             }
7905             
7906         };
7907         
7908         var box = {
7909             tag: 'div',
7910             cn: [
7911                 {
7912                     tag: 'input',
7913                     type : 'hidden',
7914                     cls: 'form-hidden-field'
7915                 },
7916                 inputblock
7917             ]
7918             
7919         };
7920         
7921         if(this.multiple){
7922             Roo.log('multiple');
7923             
7924             box = {
7925                 tag: 'div',
7926                 cn: [
7927                     {
7928                         tag: 'input',
7929                         type : 'hidden',
7930                         cls: 'form-hidden-field'
7931                     },
7932                     {
7933                         tag: 'ul',
7934                         cls: 'select2-choices',
7935                         cn:[
7936                             {
7937                                 tag: 'li',
7938                                 cls: 'select2-search-field',
7939                                 cn: [
7940
7941                                     inputblock
7942                                 ]
7943                             }
7944                         ]
7945                     }
7946                 ]
7947             }
7948         };
7949         
7950         var combobox = {
7951             cls: 'select2-container input-group',
7952             cn: [
7953                 box
7954 //                {
7955 //                    tag: 'ul',
7956 //                    cls: 'typeahead typeahead-long dropdown-menu',
7957 //                    style: 'display:none'
7958 //                }
7959             ]
7960         };
7961         
7962         if(!this.multiple && this.showToggleBtn){
7963             combobox.cn.push({
7964                 tag :'span',
7965                 cls : 'input-group-addon btn dropdown-toggle',
7966                 cn : [
7967                     {
7968                         tag: 'span',
7969                         cls: 'caret'
7970                     },
7971                     {
7972                         tag: 'span',
7973                         cls: 'combobox-clear',
7974                         cn  : [
7975                             {
7976                                 tag : 'i',
7977                                 cls: 'icon-remove'
7978                             }
7979                         ]
7980                     }
7981                 ]
7982
7983             })
7984         }
7985         
7986         if(this.multiple){
7987             combobox.cls += ' select2-container-multi';
7988         }
7989         
7990         if (align ==='left' && this.fieldLabel.length) {
7991             
7992                 Roo.log("left and has label");
7993                 cfg.cn = [
7994                     
7995                     {
7996                         tag: 'label',
7997                         'for' :  id,
7998                         cls : 'control-label col-sm-' + this.labelWidth,
7999                         html : this.fieldLabel
8000                         
8001                     },
8002                     {
8003                         cls : "col-sm-" + (12 - this.labelWidth), 
8004                         cn: [
8005                             combobox
8006                         ]
8007                     }
8008                     
8009                 ];
8010         } else if ( this.fieldLabel.length) {
8011                 Roo.log(" label");
8012                  cfg.cn = [
8013                    
8014                     {
8015                         tag: 'label',
8016                         //cls : 'input-group-addon',
8017                         html : this.fieldLabel
8018                         
8019                     },
8020                     
8021                     combobox
8022                     
8023                 ];
8024
8025         } else {
8026             
8027                 Roo.log(" no label && no align");
8028                 cfg = combobox
8029                      
8030                 
8031         }
8032          
8033         var settings=this;
8034         ['xs','sm','md','lg'].map(function(size){
8035             if (settings[size]) {
8036                 cfg.cls += ' col-' + size + '-' + settings[size];
8037             }
8038         });
8039         
8040         return cfg;
8041         
8042     },
8043     
8044     
8045     
8046     // private
8047     onResize : function(w, h){
8048 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8049 //        if(typeof w == 'number'){
8050 //            var x = w - this.trigger.getWidth();
8051 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8052 //            this.trigger.setStyle('left', x+'px');
8053 //        }
8054     },
8055
8056     // private
8057     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8058
8059     // private
8060     getResizeEl : function(){
8061         return this.inputEl();
8062     },
8063
8064     // private
8065     getPositionEl : function(){
8066         return this.inputEl();
8067     },
8068
8069     // private
8070     alignErrorIcon : function(){
8071         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8072     },
8073
8074     // private
8075     initEvents : function(){
8076         
8077         this.createList();
8078         
8079         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8080         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8081         if(!this.multiple && this.showToggleBtn){
8082             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8083             if(this.hideTrigger){
8084                 this.trigger.setDisplayed(false);
8085             }
8086             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8087         }
8088         
8089         if(this.multiple){
8090             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8091         }
8092         
8093         //this.trigger.addClassOnOver('x-form-trigger-over');
8094         //this.trigger.addClassOnClick('x-form-trigger-click');
8095         
8096         //if(!this.width){
8097         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8098         //}
8099     },
8100     
8101     createList : function()
8102     {
8103         this.list = Roo.get(document.body).createChild({
8104             tag: 'ul',
8105             cls: 'typeahead typeahead-long dropdown-menu',
8106             style: 'display:none'
8107         });
8108         
8109         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8110         
8111     },
8112
8113     // private
8114     initTrigger : function(){
8115        
8116     },
8117
8118     // private
8119     onDestroy : function(){
8120         if(this.trigger){
8121             this.trigger.removeAllListeners();
8122           //  this.trigger.remove();
8123         }
8124         //if(this.wrap){
8125         //    this.wrap.remove();
8126         //}
8127         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8128     },
8129
8130     // private
8131     onFocus : function(){
8132         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8133         /*
8134         if(!this.mimicing){
8135             this.wrap.addClass('x-trigger-wrap-focus');
8136             this.mimicing = true;
8137             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8138             if(this.monitorTab){
8139                 this.el.on("keydown", this.checkTab, this);
8140             }
8141         }
8142         */
8143     },
8144
8145     // private
8146     checkTab : function(e){
8147         if(e.getKey() == e.TAB){
8148             this.triggerBlur();
8149         }
8150     },
8151
8152     // private
8153     onBlur : function(){
8154         // do nothing
8155     },
8156
8157     // private
8158     mimicBlur : function(e, t){
8159         /*
8160         if(!this.wrap.contains(t) && this.validateBlur()){
8161             this.triggerBlur();
8162         }
8163         */
8164     },
8165
8166     // private
8167     triggerBlur : function(){
8168         this.mimicing = false;
8169         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8170         if(this.monitorTab){
8171             this.el.un("keydown", this.checkTab, this);
8172         }
8173         //this.wrap.removeClass('x-trigger-wrap-focus');
8174         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8175     },
8176
8177     // private
8178     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8179     validateBlur : function(e, t){
8180         return true;
8181     },
8182
8183     // private
8184     onDisable : function(){
8185         this.inputEl().dom.disabled = true;
8186         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8187         //if(this.wrap){
8188         //    this.wrap.addClass('x-item-disabled');
8189         //}
8190     },
8191
8192     // private
8193     onEnable : function(){
8194         this.inputEl().dom.disabled = false;
8195         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8196         //if(this.wrap){
8197         //    this.el.removeClass('x-item-disabled');
8198         //}
8199     },
8200
8201     // private
8202     onShow : function(){
8203         var ae = this.getActionEl();
8204         
8205         if(ae){
8206             ae.dom.style.display = '';
8207             ae.dom.style.visibility = 'visible';
8208         }
8209     },
8210
8211     // private
8212     
8213     onHide : function(){
8214         var ae = this.getActionEl();
8215         ae.dom.style.display = 'none';
8216     },
8217
8218     /**
8219      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8220      * by an implementing function.
8221      * @method
8222      * @param {EventObject} e
8223      */
8224     onTriggerClick : Roo.emptyFn
8225 });
8226  /*
8227  * Based on:
8228  * Ext JS Library 1.1.1
8229  * Copyright(c) 2006-2007, Ext JS, LLC.
8230  *
8231  * Originally Released Under LGPL - original licence link has changed is not relivant.
8232  *
8233  * Fork - LGPL
8234  * <script type="text/javascript">
8235  */
8236
8237
8238 /**
8239  * @class Roo.data.SortTypes
8240  * @singleton
8241  * Defines the default sorting (casting?) comparison functions used when sorting data.
8242  */
8243 Roo.data.SortTypes = {
8244     /**
8245      * Default sort that does nothing
8246      * @param {Mixed} s The value being converted
8247      * @return {Mixed} The comparison value
8248      */
8249     none : function(s){
8250         return s;
8251     },
8252     
8253     /**
8254      * The regular expression used to strip tags
8255      * @type {RegExp}
8256      * @property
8257      */
8258     stripTagsRE : /<\/?[^>]+>/gi,
8259     
8260     /**
8261      * Strips all HTML tags to sort on text only
8262      * @param {Mixed} s The value being converted
8263      * @return {String} The comparison value
8264      */
8265     asText : function(s){
8266         return String(s).replace(this.stripTagsRE, "");
8267     },
8268     
8269     /**
8270      * Strips all HTML tags to sort on text only - Case insensitive
8271      * @param {Mixed} s The value being converted
8272      * @return {String} The comparison value
8273      */
8274     asUCText : function(s){
8275         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8276     },
8277     
8278     /**
8279      * Case insensitive string
8280      * @param {Mixed} s The value being converted
8281      * @return {String} The comparison value
8282      */
8283     asUCString : function(s) {
8284         return String(s).toUpperCase();
8285     },
8286     
8287     /**
8288      * Date sorting
8289      * @param {Mixed} s The value being converted
8290      * @return {Number} The comparison value
8291      */
8292     asDate : function(s) {
8293         if(!s){
8294             return 0;
8295         }
8296         if(s instanceof Date){
8297             return s.getTime();
8298         }
8299         return Date.parse(String(s));
8300     },
8301     
8302     /**
8303      * Float sorting
8304      * @param {Mixed} s The value being converted
8305      * @return {Float} The comparison value
8306      */
8307     asFloat : function(s) {
8308         var val = parseFloat(String(s).replace(/,/g, ""));
8309         if(isNaN(val)) val = 0;
8310         return val;
8311     },
8312     
8313     /**
8314      * Integer sorting
8315      * @param {Mixed} s The value being converted
8316      * @return {Number} The comparison value
8317      */
8318     asInt : function(s) {
8319         var val = parseInt(String(s).replace(/,/g, ""));
8320         if(isNaN(val)) val = 0;
8321         return val;
8322     }
8323 };/*
8324  * Based on:
8325  * Ext JS Library 1.1.1
8326  * Copyright(c) 2006-2007, Ext JS, LLC.
8327  *
8328  * Originally Released Under LGPL - original licence link has changed is not relivant.
8329  *
8330  * Fork - LGPL
8331  * <script type="text/javascript">
8332  */
8333
8334 /**
8335 * @class Roo.data.Record
8336  * Instances of this class encapsulate both record <em>definition</em> information, and record
8337  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8338  * to access Records cached in an {@link Roo.data.Store} object.<br>
8339  * <p>
8340  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8341  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8342  * objects.<br>
8343  * <p>
8344  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8345  * @constructor
8346  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8347  * {@link #create}. The parameters are the same.
8348  * @param {Array} data An associative Array of data values keyed by the field name.
8349  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8350  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8351  * not specified an integer id is generated.
8352  */
8353 Roo.data.Record = function(data, id){
8354     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8355     this.data = data;
8356 };
8357
8358 /**
8359  * Generate a constructor for a specific record layout.
8360  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8361  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8362  * Each field definition object may contain the following properties: <ul>
8363  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
8364  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8365  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8366  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8367  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8368  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8369  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8370  * this may be omitted.</p></li>
8371  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8372  * <ul><li>auto (Default, implies no conversion)</li>
8373  * <li>string</li>
8374  * <li>int</li>
8375  * <li>float</li>
8376  * <li>boolean</li>
8377  * <li>date</li></ul></p></li>
8378  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8379  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8380  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8381  * by the Reader into an object that will be stored in the Record. It is passed the
8382  * following parameters:<ul>
8383  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8384  * </ul></p></li>
8385  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8386  * </ul>
8387  * <br>usage:<br><pre><code>
8388 var TopicRecord = Roo.data.Record.create(
8389     {name: 'title', mapping: 'topic_title'},
8390     {name: 'author', mapping: 'username'},
8391     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8392     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8393     {name: 'lastPoster', mapping: 'user2'},
8394     {name: 'excerpt', mapping: 'post_text'}
8395 );
8396
8397 var myNewRecord = new TopicRecord({
8398     title: 'Do my job please',
8399     author: 'noobie',
8400     totalPosts: 1,
8401     lastPost: new Date(),
8402     lastPoster: 'Animal',
8403     excerpt: 'No way dude!'
8404 });
8405 myStore.add(myNewRecord);
8406 </code></pre>
8407  * @method create
8408  * @static
8409  */
8410 Roo.data.Record.create = function(o){
8411     var f = function(){
8412         f.superclass.constructor.apply(this, arguments);
8413     };
8414     Roo.extend(f, Roo.data.Record);
8415     var p = f.prototype;
8416     p.fields = new Roo.util.MixedCollection(false, function(field){
8417         return field.name;
8418     });
8419     for(var i = 0, len = o.length; i < len; i++){
8420         p.fields.add(new Roo.data.Field(o[i]));
8421     }
8422     f.getField = function(name){
8423         return p.fields.get(name);  
8424     };
8425     return f;
8426 };
8427
8428 Roo.data.Record.AUTO_ID = 1000;
8429 Roo.data.Record.EDIT = 'edit';
8430 Roo.data.Record.REJECT = 'reject';
8431 Roo.data.Record.COMMIT = 'commit';
8432
8433 Roo.data.Record.prototype = {
8434     /**
8435      * Readonly flag - true if this record has been modified.
8436      * @type Boolean
8437      */
8438     dirty : false,
8439     editing : false,
8440     error: null,
8441     modified: null,
8442
8443     // private
8444     join : function(store){
8445         this.store = store;
8446     },
8447
8448     /**
8449      * Set the named field to the specified value.
8450      * @param {String} name The name of the field to set.
8451      * @param {Object} value The value to set the field to.
8452      */
8453     set : function(name, value){
8454         if(this.data[name] == value){
8455             return;
8456         }
8457         this.dirty = true;
8458         if(!this.modified){
8459             this.modified = {};
8460         }
8461         if(typeof this.modified[name] == 'undefined'){
8462             this.modified[name] = this.data[name];
8463         }
8464         this.data[name] = value;
8465         if(!this.editing && this.store){
8466             this.store.afterEdit(this);
8467         }       
8468     },
8469
8470     /**
8471      * Get the value of the named field.
8472      * @param {String} name The name of the field to get the value of.
8473      * @return {Object} The value of the field.
8474      */
8475     get : function(name){
8476         return this.data[name]; 
8477     },
8478
8479     // private
8480     beginEdit : function(){
8481         this.editing = true;
8482         this.modified = {}; 
8483     },
8484
8485     // private
8486     cancelEdit : function(){
8487         this.editing = false;
8488         delete this.modified;
8489     },
8490
8491     // private
8492     endEdit : function(){
8493         this.editing = false;
8494         if(this.dirty && this.store){
8495             this.store.afterEdit(this);
8496         }
8497     },
8498
8499     /**
8500      * Usually called by the {@link Roo.data.Store} which owns the Record.
8501      * Rejects all changes made to the Record since either creation, or the last commit operation.
8502      * Modified fields are reverted to their original values.
8503      * <p>
8504      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8505      * of reject operations.
8506      */
8507     reject : function(){
8508         var m = this.modified;
8509         for(var n in m){
8510             if(typeof m[n] != "function"){
8511                 this.data[n] = m[n];
8512             }
8513         }
8514         this.dirty = false;
8515         delete this.modified;
8516         this.editing = false;
8517         if(this.store){
8518             this.store.afterReject(this);
8519         }
8520     },
8521
8522     /**
8523      * Usually called by the {@link Roo.data.Store} which owns the Record.
8524      * Commits all changes made to the Record since either creation, or the last commit operation.
8525      * <p>
8526      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8527      * of commit operations.
8528      */
8529     commit : function(){
8530         this.dirty = false;
8531         delete this.modified;
8532         this.editing = false;
8533         if(this.store){
8534             this.store.afterCommit(this);
8535         }
8536     },
8537
8538     // private
8539     hasError : function(){
8540         return this.error != null;
8541     },
8542
8543     // private
8544     clearError : function(){
8545         this.error = null;
8546     },
8547
8548     /**
8549      * Creates a copy of this record.
8550      * @param {String} id (optional) A new record id if you don't want to use this record's id
8551      * @return {Record}
8552      */
8553     copy : function(newId) {
8554         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8555     }
8556 };/*
8557  * Based on:
8558  * Ext JS Library 1.1.1
8559  * Copyright(c) 2006-2007, Ext JS, LLC.
8560  *
8561  * Originally Released Under LGPL - original licence link has changed is not relivant.
8562  *
8563  * Fork - LGPL
8564  * <script type="text/javascript">
8565  */
8566
8567
8568
8569 /**
8570  * @class Roo.data.Store
8571  * @extends Roo.util.Observable
8572  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8573  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8574  * <p>
8575  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
8576  * has no knowledge of the format of the data returned by the Proxy.<br>
8577  * <p>
8578  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8579  * instances from the data object. These records are cached and made available through accessor functions.
8580  * @constructor
8581  * Creates a new Store.
8582  * @param {Object} config A config object containing the objects needed for the Store to access data,
8583  * and read the data into Records.
8584  */
8585 Roo.data.Store = function(config){
8586     this.data = new Roo.util.MixedCollection(false);
8587     this.data.getKey = function(o){
8588         return o.id;
8589     };
8590     this.baseParams = {};
8591     // private
8592     this.paramNames = {
8593         "start" : "start",
8594         "limit" : "limit",
8595         "sort" : "sort",
8596         "dir" : "dir",
8597         "multisort" : "_multisort"
8598     };
8599
8600     if(config && config.data){
8601         this.inlineData = config.data;
8602         delete config.data;
8603     }
8604
8605     Roo.apply(this, config);
8606     
8607     if(this.reader){ // reader passed
8608         this.reader = Roo.factory(this.reader, Roo.data);
8609         this.reader.xmodule = this.xmodule || false;
8610         if(!this.recordType){
8611             this.recordType = this.reader.recordType;
8612         }
8613         if(this.reader.onMetaChange){
8614             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8615         }
8616     }
8617
8618     if(this.recordType){
8619         this.fields = this.recordType.prototype.fields;
8620     }
8621     this.modified = [];
8622
8623     this.addEvents({
8624         /**
8625          * @event datachanged
8626          * Fires when the data cache has changed, and a widget which is using this Store
8627          * as a Record cache should refresh its view.
8628          * @param {Store} this
8629          */
8630         datachanged : true,
8631         /**
8632          * @event metachange
8633          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8634          * @param {Store} this
8635          * @param {Object} meta The JSON metadata
8636          */
8637         metachange : true,
8638         /**
8639          * @event add
8640          * Fires when Records have been added to the Store
8641          * @param {Store} this
8642          * @param {Roo.data.Record[]} records The array of Records added
8643          * @param {Number} index The index at which the record(s) were added
8644          */
8645         add : true,
8646         /**
8647          * @event remove
8648          * Fires when a Record has been removed from the Store
8649          * @param {Store} this
8650          * @param {Roo.data.Record} record The Record that was removed
8651          * @param {Number} index The index at which the record was removed
8652          */
8653         remove : true,
8654         /**
8655          * @event update
8656          * Fires when a Record has been updated
8657          * @param {Store} this
8658          * @param {Roo.data.Record} record The Record that was updated
8659          * @param {String} operation The update operation being performed.  Value may be one of:
8660          * <pre><code>
8661  Roo.data.Record.EDIT
8662  Roo.data.Record.REJECT
8663  Roo.data.Record.COMMIT
8664          * </code></pre>
8665          */
8666         update : true,
8667         /**
8668          * @event clear
8669          * Fires when the data cache has been cleared.
8670          * @param {Store} this
8671          */
8672         clear : true,
8673         /**
8674          * @event beforeload
8675          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8676          * the load action will be canceled.
8677          * @param {Store} this
8678          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8679          */
8680         beforeload : true,
8681         /**
8682          * @event beforeloadadd
8683          * Fires after a new set of Records has been loaded.
8684          * @param {Store} this
8685          * @param {Roo.data.Record[]} records The Records that were loaded
8686          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8687          */
8688         beforeloadadd : true,
8689         /**
8690          * @event load
8691          * Fires after a new set of Records has been loaded, before they are added to the store.
8692          * @param {Store} this
8693          * @param {Roo.data.Record[]} records The Records that were loaded
8694          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8695          * @params {Object} return from reader
8696          */
8697         load : true,
8698         /**
8699          * @event loadexception
8700          * Fires if an exception occurs in the Proxy during loading.
8701          * Called with the signature of the Proxy's "loadexception" event.
8702          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8703          * 
8704          * @param {Proxy} 
8705          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8706          * @param {Object} load options 
8707          * @param {Object} jsonData from your request (normally this contains the Exception)
8708          */
8709         loadexception : true
8710     });
8711     
8712     if(this.proxy){
8713         this.proxy = Roo.factory(this.proxy, Roo.data);
8714         this.proxy.xmodule = this.xmodule || false;
8715         this.relayEvents(this.proxy,  ["loadexception"]);
8716     }
8717     this.sortToggle = {};
8718     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8719
8720     Roo.data.Store.superclass.constructor.call(this);
8721
8722     if(this.inlineData){
8723         this.loadData(this.inlineData);
8724         delete this.inlineData;
8725     }
8726 };
8727
8728 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8729      /**
8730     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8731     * without a remote query - used by combo/forms at present.
8732     */
8733     
8734     /**
8735     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8736     */
8737     /**
8738     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8739     */
8740     /**
8741     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8742     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8743     */
8744     /**
8745     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8746     * on any HTTP request
8747     */
8748     /**
8749     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8750     */
8751     /**
8752     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8753     */
8754     multiSort: false,
8755     /**
8756     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8757     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8758     */
8759     remoteSort : false,
8760
8761     /**
8762     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8763      * loaded or when a record is removed. (defaults to false).
8764     */
8765     pruneModifiedRecords : false,
8766
8767     // private
8768     lastOptions : null,
8769
8770     /**
8771      * Add Records to the Store and fires the add event.
8772      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8773      */
8774     add : function(records){
8775         records = [].concat(records);
8776         for(var i = 0, len = records.length; i < len; i++){
8777             records[i].join(this);
8778         }
8779         var index = this.data.length;
8780         this.data.addAll(records);
8781         this.fireEvent("add", this, records, index);
8782     },
8783
8784     /**
8785      * Remove a Record from the Store and fires the remove event.
8786      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8787      */
8788     remove : function(record){
8789         var index = this.data.indexOf(record);
8790         this.data.removeAt(index);
8791         if(this.pruneModifiedRecords){
8792             this.modified.remove(record);
8793         }
8794         this.fireEvent("remove", this, record, index);
8795     },
8796
8797     /**
8798      * Remove all Records from the Store and fires the clear event.
8799      */
8800     removeAll : function(){
8801         this.data.clear();
8802         if(this.pruneModifiedRecords){
8803             this.modified = [];
8804         }
8805         this.fireEvent("clear", this);
8806     },
8807
8808     /**
8809      * Inserts Records to the Store at the given index and fires the add event.
8810      * @param {Number} index The start index at which to insert the passed Records.
8811      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8812      */
8813     insert : function(index, records){
8814         records = [].concat(records);
8815         for(var i = 0, len = records.length; i < len; i++){
8816             this.data.insert(index, records[i]);
8817             records[i].join(this);
8818         }
8819         this.fireEvent("add", this, records, index);
8820     },
8821
8822     /**
8823      * Get the index within the cache of the passed Record.
8824      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8825      * @return {Number} The index of the passed Record. Returns -1 if not found.
8826      */
8827     indexOf : function(record){
8828         return this.data.indexOf(record);
8829     },
8830
8831     /**
8832      * Get the index within the cache of the Record with the passed id.
8833      * @param {String} id The id of the Record to find.
8834      * @return {Number} The index of the Record. Returns -1 if not found.
8835      */
8836     indexOfId : function(id){
8837         return this.data.indexOfKey(id);
8838     },
8839
8840     /**
8841      * Get the Record with the specified id.
8842      * @param {String} id The id of the Record to find.
8843      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8844      */
8845     getById : function(id){
8846         return this.data.key(id);
8847     },
8848
8849     /**
8850      * Get the Record at the specified index.
8851      * @param {Number} index The index of the Record to find.
8852      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8853      */
8854     getAt : function(index){
8855         return this.data.itemAt(index);
8856     },
8857
8858     /**
8859      * Returns a range of Records between specified indices.
8860      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8861      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8862      * @return {Roo.data.Record[]} An array of Records
8863      */
8864     getRange : function(start, end){
8865         return this.data.getRange(start, end);
8866     },
8867
8868     // private
8869     storeOptions : function(o){
8870         o = Roo.apply({}, o);
8871         delete o.callback;
8872         delete o.scope;
8873         this.lastOptions = o;
8874     },
8875
8876     /**
8877      * Loads the Record cache from the configured Proxy using the configured Reader.
8878      * <p>
8879      * If using remote paging, then the first load call must specify the <em>start</em>
8880      * and <em>limit</em> properties in the options.params property to establish the initial
8881      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8882      * <p>
8883      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8884      * and this call will return before the new data has been loaded. Perform any post-processing
8885      * in a callback function, or in a "load" event handler.</strong>
8886      * <p>
8887      * @param {Object} options An object containing properties which control loading options:<ul>
8888      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8889      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8890      * passed the following arguments:<ul>
8891      * <li>r : Roo.data.Record[]</li>
8892      * <li>options: Options object from the load call</li>
8893      * <li>success: Boolean success indicator</li></ul></li>
8894      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8895      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8896      * </ul>
8897      */
8898     load : function(options){
8899         options = options || {};
8900         if(this.fireEvent("beforeload", this, options) !== false){
8901             this.storeOptions(options);
8902             var p = Roo.apply(options.params || {}, this.baseParams);
8903             // if meta was not loaded from remote source.. try requesting it.
8904             if (!this.reader.metaFromRemote) {
8905                 p._requestMeta = 1;
8906             }
8907             if(this.sortInfo && this.remoteSort){
8908                 var pn = this.paramNames;
8909                 p[pn["sort"]] = this.sortInfo.field;
8910                 p[pn["dir"]] = this.sortInfo.direction;
8911             }
8912             if (this.multiSort) {
8913                 var pn = this.paramNames;
8914                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8915             }
8916             
8917             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8918         }
8919     },
8920
8921     /**
8922      * Reloads the Record cache from the configured Proxy using the configured Reader and
8923      * the options from the last load operation performed.
8924      * @param {Object} options (optional) An object containing properties which may override the options
8925      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8926      * the most recently used options are reused).
8927      */
8928     reload : function(options){
8929         this.load(Roo.applyIf(options||{}, this.lastOptions));
8930     },
8931
8932     // private
8933     // Called as a callback by the Reader during a load operation.
8934     loadRecords : function(o, options, success){
8935         if(!o || success === false){
8936             if(success !== false){
8937                 this.fireEvent("load", this, [], options, o);
8938             }
8939             if(options.callback){
8940                 options.callback.call(options.scope || this, [], options, false);
8941             }
8942             return;
8943         }
8944         // if data returned failure - throw an exception.
8945         if (o.success === false) {
8946             // show a message if no listener is registered.
8947             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8948                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8949             }
8950             // loadmask wil be hooked into this..
8951             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8952             return;
8953         }
8954         var r = o.records, t = o.totalRecords || r.length;
8955         
8956         this.fireEvent("beforeloadadd", this, r, options, o);
8957         
8958         if(!options || options.add !== true){
8959             if(this.pruneModifiedRecords){
8960                 this.modified = [];
8961             }
8962             for(var i = 0, len = r.length; i < len; i++){
8963                 r[i].join(this);
8964             }
8965             if(this.snapshot){
8966                 this.data = this.snapshot;
8967                 delete this.snapshot;
8968             }
8969             this.data.clear();
8970             this.data.addAll(r);
8971             this.totalLength = t;
8972             this.applySort();
8973             this.fireEvent("datachanged", this);
8974         }else{
8975             this.totalLength = Math.max(t, this.data.length+r.length);
8976             this.add(r);
8977         }
8978         this.fireEvent("load", this, r, options, o);
8979         if(options.callback){
8980             options.callback.call(options.scope || this, r, options, true);
8981         }
8982     },
8983
8984
8985     /**
8986      * Loads data from a passed data block. A Reader which understands the format of the data
8987      * must have been configured in the constructor.
8988      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8989      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8990      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8991      */
8992     loadData : function(o, append){
8993         var r = this.reader.readRecords(o);
8994         this.loadRecords(r, {add: append}, true);
8995     },
8996
8997     /**
8998      * Gets the number of cached records.
8999      * <p>
9000      * <em>If using paging, this may not be the total size of the dataset. If the data object
9001      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9002      * the data set size</em>
9003      */
9004     getCount : function(){
9005         return this.data.length || 0;
9006     },
9007
9008     /**
9009      * Gets the total number of records in the dataset as returned by the server.
9010      * <p>
9011      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9012      * the dataset size</em>
9013      */
9014     getTotalCount : function(){
9015         return this.totalLength || 0;
9016     },
9017
9018     /**
9019      * Returns the sort state of the Store as an object with two properties:
9020      * <pre><code>
9021  field {String} The name of the field by which the Records are sorted
9022  direction {String} The sort order, "ASC" or "DESC"
9023      * </code></pre>
9024      */
9025     getSortState : function(){
9026         return this.sortInfo;
9027     },
9028
9029     // private
9030     applySort : function(){
9031         if(this.sortInfo && !this.remoteSort){
9032             var s = this.sortInfo, f = s.field;
9033             var st = this.fields.get(f).sortType;
9034             var fn = function(r1, r2){
9035                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9036                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9037             };
9038             this.data.sort(s.direction, fn);
9039             if(this.snapshot && this.snapshot != this.data){
9040                 this.snapshot.sort(s.direction, fn);
9041             }
9042         }
9043     },
9044
9045     /**
9046      * Sets the default sort column and order to be used by the next load operation.
9047      * @param {String} fieldName The name of the field to sort by.
9048      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9049      */
9050     setDefaultSort : function(field, dir){
9051         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9052     },
9053
9054     /**
9055      * Sort the Records.
9056      * If remote sorting is used, the sort is performed on the server, and the cache is
9057      * reloaded. If local sorting is used, the cache is sorted internally.
9058      * @param {String} fieldName The name of the field to sort by.
9059      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9060      */
9061     sort : function(fieldName, dir){
9062         var f = this.fields.get(fieldName);
9063         if(!dir){
9064             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9065             
9066             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9067                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9068             }else{
9069                 dir = f.sortDir;
9070             }
9071         }
9072         this.sortToggle[f.name] = dir;
9073         this.sortInfo = {field: f.name, direction: dir};
9074         if(!this.remoteSort){
9075             this.applySort();
9076             this.fireEvent("datachanged", this);
9077         }else{
9078             this.load(this.lastOptions);
9079         }
9080     },
9081
9082     /**
9083      * Calls the specified function for each of the Records in the cache.
9084      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9085      * Returning <em>false</em> aborts and exits the iteration.
9086      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9087      */
9088     each : function(fn, scope){
9089         this.data.each(fn, scope);
9090     },
9091
9092     /**
9093      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9094      * (e.g., during paging).
9095      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9096      */
9097     getModifiedRecords : function(){
9098         return this.modified;
9099     },
9100
9101     // private
9102     createFilterFn : function(property, value, anyMatch){
9103         if(!value.exec){ // not a regex
9104             value = String(value);
9105             if(value.length == 0){
9106                 return false;
9107             }
9108             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9109         }
9110         return function(r){
9111             return value.test(r.data[property]);
9112         };
9113     },
9114
9115     /**
9116      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9117      * @param {String} property A field on your records
9118      * @param {Number} start The record index to start at (defaults to 0)
9119      * @param {Number} end The last record index to include (defaults to length - 1)
9120      * @return {Number} The sum
9121      */
9122     sum : function(property, start, end){
9123         var rs = this.data.items, v = 0;
9124         start = start || 0;
9125         end = (end || end === 0) ? end : rs.length-1;
9126
9127         for(var i = start; i <= end; i++){
9128             v += (rs[i].data[property] || 0);
9129         }
9130         return v;
9131     },
9132
9133     /**
9134      * Filter the records by a specified property.
9135      * @param {String} field A field on your records
9136      * @param {String/RegExp} value Either a string that the field
9137      * should start with or a RegExp to test against the field
9138      * @param {Boolean} anyMatch True to match any part not just the beginning
9139      */
9140     filter : function(property, value, anyMatch){
9141         var fn = this.createFilterFn(property, value, anyMatch);
9142         return fn ? this.filterBy(fn) : this.clearFilter();
9143     },
9144
9145     /**
9146      * Filter by a function. The specified function will be called with each
9147      * record in this data source. If the function returns true the record is included,
9148      * otherwise it is filtered.
9149      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9150      * @param {Object} scope (optional) The scope of the function (defaults to this)
9151      */
9152     filterBy : function(fn, scope){
9153         this.snapshot = this.snapshot || this.data;
9154         this.data = this.queryBy(fn, scope||this);
9155         this.fireEvent("datachanged", this);
9156     },
9157
9158     /**
9159      * Query the records by a specified property.
9160      * @param {String} field A field on your records
9161      * @param {String/RegExp} value Either a string that the field
9162      * should start with or a RegExp to test against the field
9163      * @param {Boolean} anyMatch True to match any part not just the beginning
9164      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9165      */
9166     query : function(property, value, anyMatch){
9167         var fn = this.createFilterFn(property, value, anyMatch);
9168         return fn ? this.queryBy(fn) : this.data.clone();
9169     },
9170
9171     /**
9172      * Query by a function. The specified function will be called with each
9173      * record in this data source. If the function returns true the record is included
9174      * in the results.
9175      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9176      * @param {Object} scope (optional) The scope of the function (defaults to this)
9177       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9178      **/
9179     queryBy : function(fn, scope){
9180         var data = this.snapshot || this.data;
9181         return data.filterBy(fn, scope||this);
9182     },
9183
9184     /**
9185      * Collects unique values for a particular dataIndex from this store.
9186      * @param {String} dataIndex The property to collect
9187      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9188      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9189      * @return {Array} An array of the unique values
9190      **/
9191     collect : function(dataIndex, allowNull, bypassFilter){
9192         var d = (bypassFilter === true && this.snapshot) ?
9193                 this.snapshot.items : this.data.items;
9194         var v, sv, r = [], l = {};
9195         for(var i = 0, len = d.length; i < len; i++){
9196             v = d[i].data[dataIndex];
9197             sv = String(v);
9198             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9199                 l[sv] = true;
9200                 r[r.length] = v;
9201             }
9202         }
9203         return r;
9204     },
9205
9206     /**
9207      * Revert to a view of the Record cache with no filtering applied.
9208      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9209      */
9210     clearFilter : function(suppressEvent){
9211         if(this.snapshot && this.snapshot != this.data){
9212             this.data = this.snapshot;
9213             delete this.snapshot;
9214             if(suppressEvent !== true){
9215                 this.fireEvent("datachanged", this);
9216             }
9217         }
9218     },
9219
9220     // private
9221     afterEdit : function(record){
9222         if(this.modified.indexOf(record) == -1){
9223             this.modified.push(record);
9224         }
9225         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9226     },
9227     
9228     // private
9229     afterReject : function(record){
9230         this.modified.remove(record);
9231         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9232     },
9233
9234     // private
9235     afterCommit : function(record){
9236         this.modified.remove(record);
9237         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9238     },
9239
9240     /**
9241      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9242      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9243      */
9244     commitChanges : function(){
9245         var m = this.modified.slice(0);
9246         this.modified = [];
9247         for(var i = 0, len = m.length; i < len; i++){
9248             m[i].commit();
9249         }
9250     },
9251
9252     /**
9253      * Cancel outstanding changes on all changed records.
9254      */
9255     rejectChanges : function(){
9256         var m = this.modified.slice(0);
9257         this.modified = [];
9258         for(var i = 0, len = m.length; i < len; i++){
9259             m[i].reject();
9260         }
9261     },
9262
9263     onMetaChange : function(meta, rtype, o){
9264         this.recordType = rtype;
9265         this.fields = rtype.prototype.fields;
9266         delete this.snapshot;
9267         this.sortInfo = meta.sortInfo || this.sortInfo;
9268         this.modified = [];
9269         this.fireEvent('metachange', this, this.reader.meta);
9270     },
9271     
9272     moveIndex : function(data, type)
9273     {
9274         var index = this.indexOf(data);
9275         
9276         var newIndex = index + type;
9277         
9278         this.remove(data);
9279         
9280         this.insert(newIndex, data);
9281         
9282     }
9283 });/*
9284  * Based on:
9285  * Ext JS Library 1.1.1
9286  * Copyright(c) 2006-2007, Ext JS, LLC.
9287  *
9288  * Originally Released Under LGPL - original licence link has changed is not relivant.
9289  *
9290  * Fork - LGPL
9291  * <script type="text/javascript">
9292  */
9293
9294 /**
9295  * @class Roo.data.SimpleStore
9296  * @extends Roo.data.Store
9297  * Small helper class to make creating Stores from Array data easier.
9298  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9299  * @cfg {Array} fields An array of field definition objects, or field name strings.
9300  * @cfg {Array} data The multi-dimensional array of data
9301  * @constructor
9302  * @param {Object} config
9303  */
9304 Roo.data.SimpleStore = function(config){
9305     Roo.data.SimpleStore.superclass.constructor.call(this, {
9306         isLocal : true,
9307         reader: new Roo.data.ArrayReader({
9308                 id: config.id
9309             },
9310             Roo.data.Record.create(config.fields)
9311         ),
9312         proxy : new Roo.data.MemoryProxy(config.data)
9313     });
9314     this.load();
9315 };
9316 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9317  * Based on:
9318  * Ext JS Library 1.1.1
9319  * Copyright(c) 2006-2007, Ext JS, LLC.
9320  *
9321  * Originally Released Under LGPL - original licence link has changed is not relivant.
9322  *
9323  * Fork - LGPL
9324  * <script type="text/javascript">
9325  */
9326
9327 /**
9328 /**
9329  * @extends Roo.data.Store
9330  * @class Roo.data.JsonStore
9331  * Small helper class to make creating Stores for JSON data easier. <br/>
9332 <pre><code>
9333 var store = new Roo.data.JsonStore({
9334     url: 'get-images.php',
9335     root: 'images',
9336     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9337 });
9338 </code></pre>
9339  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9340  * JsonReader and HttpProxy (unless inline data is provided).</b>
9341  * @cfg {Array} fields An array of field definition objects, or field name strings.
9342  * @constructor
9343  * @param {Object} config
9344  */
9345 Roo.data.JsonStore = function(c){
9346     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9347         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9348         reader: new Roo.data.JsonReader(c, c.fields)
9349     }));
9350 };
9351 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9352  * Based on:
9353  * Ext JS Library 1.1.1
9354  * Copyright(c) 2006-2007, Ext JS, LLC.
9355  *
9356  * Originally Released Under LGPL - original licence link has changed is not relivant.
9357  *
9358  * Fork - LGPL
9359  * <script type="text/javascript">
9360  */
9361
9362  
9363 Roo.data.Field = function(config){
9364     if(typeof config == "string"){
9365         config = {name: config};
9366     }
9367     Roo.apply(this, config);
9368     
9369     if(!this.type){
9370         this.type = "auto";
9371     }
9372     
9373     var st = Roo.data.SortTypes;
9374     // named sortTypes are supported, here we look them up
9375     if(typeof this.sortType == "string"){
9376         this.sortType = st[this.sortType];
9377     }
9378     
9379     // set default sortType for strings and dates
9380     if(!this.sortType){
9381         switch(this.type){
9382             case "string":
9383                 this.sortType = st.asUCString;
9384                 break;
9385             case "date":
9386                 this.sortType = st.asDate;
9387                 break;
9388             default:
9389                 this.sortType = st.none;
9390         }
9391     }
9392
9393     // define once
9394     var stripRe = /[\$,%]/g;
9395
9396     // prebuilt conversion function for this field, instead of
9397     // switching every time we're reading a value
9398     if(!this.convert){
9399         var cv, dateFormat = this.dateFormat;
9400         switch(this.type){
9401             case "":
9402             case "auto":
9403             case undefined:
9404                 cv = function(v){ return v; };
9405                 break;
9406             case "string":
9407                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9408                 break;
9409             case "int":
9410                 cv = function(v){
9411                     return v !== undefined && v !== null && v !== '' ?
9412                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9413                     };
9414                 break;
9415             case "float":
9416                 cv = function(v){
9417                     return v !== undefined && v !== null && v !== '' ?
9418                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9419                     };
9420                 break;
9421             case "bool":
9422             case "boolean":
9423                 cv = function(v){ return v === true || v === "true" || v == 1; };
9424                 break;
9425             case "date":
9426                 cv = function(v){
9427                     if(!v){
9428                         return '';
9429                     }
9430                     if(v instanceof Date){
9431                         return v;
9432                     }
9433                     if(dateFormat){
9434                         if(dateFormat == "timestamp"){
9435                             return new Date(v*1000);
9436                         }
9437                         return Date.parseDate(v, dateFormat);
9438                     }
9439                     var parsed = Date.parse(v);
9440                     return parsed ? new Date(parsed) : null;
9441                 };
9442              break;
9443             
9444         }
9445         this.convert = cv;
9446     }
9447 };
9448
9449 Roo.data.Field.prototype = {
9450     dateFormat: null,
9451     defaultValue: "",
9452     mapping: null,
9453     sortType : null,
9454     sortDir : "ASC"
9455 };/*
9456  * Based on:
9457  * Ext JS Library 1.1.1
9458  * Copyright(c) 2006-2007, Ext JS, LLC.
9459  *
9460  * Originally Released Under LGPL - original licence link has changed is not relivant.
9461  *
9462  * Fork - LGPL
9463  * <script type="text/javascript">
9464  */
9465  
9466 // Base class for reading structured data from a data source.  This class is intended to be
9467 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9468
9469 /**
9470  * @class Roo.data.DataReader
9471  * Base class for reading structured data from a data source.  This class is intended to be
9472  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9473  */
9474
9475 Roo.data.DataReader = function(meta, recordType){
9476     
9477     this.meta = meta;
9478     
9479     this.recordType = recordType instanceof Array ? 
9480         Roo.data.Record.create(recordType) : recordType;
9481 };
9482
9483 Roo.data.DataReader.prototype = {
9484      /**
9485      * Create an empty record
9486      * @param {Object} data (optional) - overlay some values
9487      * @return {Roo.data.Record} record created.
9488      */
9489     newRow :  function(d) {
9490         var da =  {};
9491         this.recordType.prototype.fields.each(function(c) {
9492             switch( c.type) {
9493                 case 'int' : da[c.name] = 0; break;
9494                 case 'date' : da[c.name] = new Date(); break;
9495                 case 'float' : da[c.name] = 0.0; break;
9496                 case 'boolean' : da[c.name] = false; break;
9497                 default : da[c.name] = ""; break;
9498             }
9499             
9500         });
9501         return new this.recordType(Roo.apply(da, d));
9502     }
9503     
9504 };/*
9505  * Based on:
9506  * Ext JS Library 1.1.1
9507  * Copyright(c) 2006-2007, Ext JS, LLC.
9508  *
9509  * Originally Released Under LGPL - original licence link has changed is not relivant.
9510  *
9511  * Fork - LGPL
9512  * <script type="text/javascript">
9513  */
9514
9515 /**
9516  * @class Roo.data.DataProxy
9517  * @extends Roo.data.Observable
9518  * This class is an abstract base class for implementations which provide retrieval of
9519  * unformatted data objects.<br>
9520  * <p>
9521  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9522  * (of the appropriate type which knows how to parse the data object) to provide a block of
9523  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9524  * <p>
9525  * Custom implementations must implement the load method as described in
9526  * {@link Roo.data.HttpProxy#load}.
9527  */
9528 Roo.data.DataProxy = function(){
9529     this.addEvents({
9530         /**
9531          * @event beforeload
9532          * Fires before a network request is made to retrieve a data object.
9533          * @param {Object} This DataProxy object.
9534          * @param {Object} params The params parameter to the load function.
9535          */
9536         beforeload : true,
9537         /**
9538          * @event load
9539          * Fires before the load method's callback is called.
9540          * @param {Object} This DataProxy object.
9541          * @param {Object} o The data object.
9542          * @param {Object} arg The callback argument object passed to the load function.
9543          */
9544         load : true,
9545         /**
9546          * @event loadexception
9547          * Fires if an Exception occurs during data retrieval.
9548          * @param {Object} This DataProxy object.
9549          * @param {Object} o The data object.
9550          * @param {Object} arg The callback argument object passed to the load function.
9551          * @param {Object} e The Exception.
9552          */
9553         loadexception : true
9554     });
9555     Roo.data.DataProxy.superclass.constructor.call(this);
9556 };
9557
9558 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9559
9560     /**
9561      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9562      */
9563 /*
9564  * Based on:
9565  * Ext JS Library 1.1.1
9566  * Copyright(c) 2006-2007, Ext JS, LLC.
9567  *
9568  * Originally Released Under LGPL - original licence link has changed is not relivant.
9569  *
9570  * Fork - LGPL
9571  * <script type="text/javascript">
9572  */
9573 /**
9574  * @class Roo.data.MemoryProxy
9575  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9576  * to the Reader when its load method is called.
9577  * @constructor
9578  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9579  */
9580 Roo.data.MemoryProxy = function(data){
9581     if (data.data) {
9582         data = data.data;
9583     }
9584     Roo.data.MemoryProxy.superclass.constructor.call(this);
9585     this.data = data;
9586 };
9587
9588 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9589     /**
9590      * Load data from the requested source (in this case an in-memory
9591      * data object passed to the constructor), read the data object into
9592      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9593      * process that block using the passed callback.
9594      * @param {Object} params This parameter is not used by the MemoryProxy class.
9595      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9596      * object into a block of Roo.data.Records.
9597      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9598      * The function must be passed <ul>
9599      * <li>The Record block object</li>
9600      * <li>The "arg" argument from the load function</li>
9601      * <li>A boolean success indicator</li>
9602      * </ul>
9603      * @param {Object} scope The scope in which to call the callback
9604      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9605      */
9606     load : function(params, reader, callback, scope, arg){
9607         params = params || {};
9608         var result;
9609         try {
9610             result = reader.readRecords(this.data);
9611         }catch(e){
9612             this.fireEvent("loadexception", this, arg, null, e);
9613             callback.call(scope, null, arg, false);
9614             return;
9615         }
9616         callback.call(scope, result, arg, true);
9617     },
9618     
9619     // private
9620     update : function(params, records){
9621         
9622     }
9623 });/*
9624  * Based on:
9625  * Ext JS Library 1.1.1
9626  * Copyright(c) 2006-2007, Ext JS, LLC.
9627  *
9628  * Originally Released Under LGPL - original licence link has changed is not relivant.
9629  *
9630  * Fork - LGPL
9631  * <script type="text/javascript">
9632  */
9633 /**
9634  * @class Roo.data.HttpProxy
9635  * @extends Roo.data.DataProxy
9636  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9637  * configured to reference a certain URL.<br><br>
9638  * <p>
9639  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9640  * from which the running page was served.<br><br>
9641  * <p>
9642  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9643  * <p>
9644  * Be aware that to enable the browser to parse an XML document, the server must set
9645  * the Content-Type header in the HTTP response to "text/xml".
9646  * @constructor
9647  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9648  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9649  * will be used to make the request.
9650  */
9651 Roo.data.HttpProxy = function(conn){
9652     Roo.data.HttpProxy.superclass.constructor.call(this);
9653     // is conn a conn config or a real conn?
9654     this.conn = conn;
9655     this.useAjax = !conn || !conn.events;
9656   
9657 };
9658
9659 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9660     // thse are take from connection...
9661     
9662     /**
9663      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9664      */
9665     /**
9666      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9667      * extra parameters to each request made by this object. (defaults to undefined)
9668      */
9669     /**
9670      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9671      *  to each request made by this object. (defaults to undefined)
9672      */
9673     /**
9674      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
9675      */
9676     /**
9677      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9678      */
9679      /**
9680      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9681      * @type Boolean
9682      */
9683   
9684
9685     /**
9686      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9687      * @type Boolean
9688      */
9689     /**
9690      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9691      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9692      * a finer-grained basis than the DataProxy events.
9693      */
9694     getConnection : function(){
9695         return this.useAjax ? Roo.Ajax : this.conn;
9696     },
9697
9698     /**
9699      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9700      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9701      * process that block using the passed callback.
9702      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9703      * for the request to the remote server.
9704      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9705      * object into a block of Roo.data.Records.
9706      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9707      * The function must be passed <ul>
9708      * <li>The Record block object</li>
9709      * <li>The "arg" argument from the load function</li>
9710      * <li>A boolean success indicator</li>
9711      * </ul>
9712      * @param {Object} scope The scope in which to call the callback
9713      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9714      */
9715     load : function(params, reader, callback, scope, arg){
9716         if(this.fireEvent("beforeload", this, params) !== false){
9717             var  o = {
9718                 params : params || {},
9719                 request: {
9720                     callback : callback,
9721                     scope : scope,
9722                     arg : arg
9723                 },
9724                 reader: reader,
9725                 callback : this.loadResponse,
9726                 scope: this
9727             };
9728             if(this.useAjax){
9729                 Roo.applyIf(o, this.conn);
9730                 if(this.activeRequest){
9731                     Roo.Ajax.abort(this.activeRequest);
9732                 }
9733                 this.activeRequest = Roo.Ajax.request(o);
9734             }else{
9735                 this.conn.request(o);
9736             }
9737         }else{
9738             callback.call(scope||this, null, arg, false);
9739         }
9740     },
9741
9742     // private
9743     loadResponse : function(o, success, response){
9744         delete this.activeRequest;
9745         if(!success){
9746             this.fireEvent("loadexception", this, o, response);
9747             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9748             return;
9749         }
9750         var result;
9751         try {
9752             result = o.reader.read(response);
9753         }catch(e){
9754             this.fireEvent("loadexception", this, o, response, e);
9755             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9756             return;
9757         }
9758         
9759         this.fireEvent("load", this, o, o.request.arg);
9760         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9761     },
9762
9763     // private
9764     update : function(dataSet){
9765
9766     },
9767
9768     // private
9769     updateResponse : function(dataSet){
9770
9771     }
9772 });/*
9773  * Based on:
9774  * Ext JS Library 1.1.1
9775  * Copyright(c) 2006-2007, Ext JS, LLC.
9776  *
9777  * Originally Released Under LGPL - original licence link has changed is not relivant.
9778  *
9779  * Fork - LGPL
9780  * <script type="text/javascript">
9781  */
9782
9783 /**
9784  * @class Roo.data.ScriptTagProxy
9785  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9786  * other than the originating domain of the running page.<br><br>
9787  * <p>
9788  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
9789  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9790  * <p>
9791  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9792  * source code that is used as the source inside a &lt;script> tag.<br><br>
9793  * <p>
9794  * In order for the browser to process the returned data, the server must wrap the data object
9795  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9796  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9797  * depending on whether the callback name was passed:
9798  * <p>
9799  * <pre><code>
9800 boolean scriptTag = false;
9801 String cb = request.getParameter("callback");
9802 if (cb != null) {
9803     scriptTag = true;
9804     response.setContentType("text/javascript");
9805 } else {
9806     response.setContentType("application/x-json");
9807 }
9808 Writer out = response.getWriter();
9809 if (scriptTag) {
9810     out.write(cb + "(");
9811 }
9812 out.print(dataBlock.toJsonString());
9813 if (scriptTag) {
9814     out.write(");");
9815 }
9816 </pre></code>
9817  *
9818  * @constructor
9819  * @param {Object} config A configuration object.
9820  */
9821 Roo.data.ScriptTagProxy = function(config){
9822     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9823     Roo.apply(this, config);
9824     this.head = document.getElementsByTagName("head")[0];
9825 };
9826
9827 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9828
9829 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9830     /**
9831      * @cfg {String} url The URL from which to request the data object.
9832      */
9833     /**
9834      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9835      */
9836     timeout : 30000,
9837     /**
9838      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9839      * the server the name of the callback function set up by the load call to process the returned data object.
9840      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9841      * javascript output which calls this named function passing the data object as its only parameter.
9842      */
9843     callbackParam : "callback",
9844     /**
9845      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9846      * name to the request.
9847      */
9848     nocache : true,
9849
9850     /**
9851      * Load data from the configured URL, read the data object into
9852      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9853      * process that block using the passed callback.
9854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9855      * for the request to the remote server.
9856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9857      * object into a block of Roo.data.Records.
9858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9859      * The function must be passed <ul>
9860      * <li>The Record block object</li>
9861      * <li>The "arg" argument from the load function</li>
9862      * <li>A boolean success indicator</li>
9863      * </ul>
9864      * @param {Object} scope The scope in which to call the callback
9865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9866      */
9867     load : function(params, reader, callback, scope, arg){
9868         if(this.fireEvent("beforeload", this, params) !== false){
9869
9870             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9871
9872             var url = this.url;
9873             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9874             if(this.nocache){
9875                 url += "&_dc=" + (new Date().getTime());
9876             }
9877             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9878             var trans = {
9879                 id : transId,
9880                 cb : "stcCallback"+transId,
9881                 scriptId : "stcScript"+transId,
9882                 params : params,
9883                 arg : arg,
9884                 url : url,
9885                 callback : callback,
9886                 scope : scope,
9887                 reader : reader
9888             };
9889             var conn = this;
9890
9891             window[trans.cb] = function(o){
9892                 conn.handleResponse(o, trans);
9893             };
9894
9895             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9896
9897             if(this.autoAbort !== false){
9898                 this.abort();
9899             }
9900
9901             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9902
9903             var script = document.createElement("script");
9904             script.setAttribute("src", url);
9905             script.setAttribute("type", "text/javascript");
9906             script.setAttribute("id", trans.scriptId);
9907             this.head.appendChild(script);
9908
9909             this.trans = trans;
9910         }else{
9911             callback.call(scope||this, null, arg, false);
9912         }
9913     },
9914
9915     // private
9916     isLoading : function(){
9917         return this.trans ? true : false;
9918     },
9919
9920     /**
9921      * Abort the current server request.
9922      */
9923     abort : function(){
9924         if(this.isLoading()){
9925             this.destroyTrans(this.trans);
9926         }
9927     },
9928
9929     // private
9930     destroyTrans : function(trans, isLoaded){
9931         this.head.removeChild(document.getElementById(trans.scriptId));
9932         clearTimeout(trans.timeoutId);
9933         if(isLoaded){
9934             window[trans.cb] = undefined;
9935             try{
9936                 delete window[trans.cb];
9937             }catch(e){}
9938         }else{
9939             // if hasn't been loaded, wait for load to remove it to prevent script error
9940             window[trans.cb] = function(){
9941                 window[trans.cb] = undefined;
9942                 try{
9943                     delete window[trans.cb];
9944                 }catch(e){}
9945             };
9946         }
9947     },
9948
9949     // private
9950     handleResponse : function(o, trans){
9951         this.trans = false;
9952         this.destroyTrans(trans, true);
9953         var result;
9954         try {
9955             result = trans.reader.readRecords(o);
9956         }catch(e){
9957             this.fireEvent("loadexception", this, o, trans.arg, e);
9958             trans.callback.call(trans.scope||window, null, trans.arg, false);
9959             return;
9960         }
9961         this.fireEvent("load", this, o, trans.arg);
9962         trans.callback.call(trans.scope||window, result, trans.arg, true);
9963     },
9964
9965     // private
9966     handleFailure : function(trans){
9967         this.trans = false;
9968         this.destroyTrans(trans, false);
9969         this.fireEvent("loadexception", this, null, trans.arg);
9970         trans.callback.call(trans.scope||window, null, trans.arg, false);
9971     }
9972 });/*
9973  * Based on:
9974  * Ext JS Library 1.1.1
9975  * Copyright(c) 2006-2007, Ext JS, LLC.
9976  *
9977  * Originally Released Under LGPL - original licence link has changed is not relivant.
9978  *
9979  * Fork - LGPL
9980  * <script type="text/javascript">
9981  */
9982
9983 /**
9984  * @class Roo.data.JsonReader
9985  * @extends Roo.data.DataReader
9986  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9987  * based on mappings in a provided Roo.data.Record constructor.
9988  * 
9989  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9990  * in the reply previously. 
9991  * 
9992  * <p>
9993  * Example code:
9994  * <pre><code>
9995 var RecordDef = Roo.data.Record.create([
9996     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9997     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9998 ]);
9999 var myReader = new Roo.data.JsonReader({
10000     totalProperty: "results",    // The property which contains the total dataset size (optional)
10001     root: "rows",                // The property which contains an Array of row objects
10002     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10003 }, RecordDef);
10004 </code></pre>
10005  * <p>
10006  * This would consume a JSON file like this:
10007  * <pre><code>
10008 { 'results': 2, 'rows': [
10009     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10010     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10011 }
10012 </code></pre>
10013  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10014  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10015  * paged from the remote server.
10016  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10017  * @cfg {String} root name of the property which contains the Array of row objects.
10018  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10019  * @constructor
10020  * Create a new JsonReader
10021  * @param {Object} meta Metadata configuration options
10022  * @param {Object} recordType Either an Array of field definition objects,
10023  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10024  */
10025 Roo.data.JsonReader = function(meta, recordType){
10026     
10027     meta = meta || {};
10028     // set some defaults:
10029     Roo.applyIf(meta, {
10030         totalProperty: 'total',
10031         successProperty : 'success',
10032         root : 'data',
10033         id : 'id'
10034     });
10035     
10036     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10037 };
10038 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10039     
10040     /**
10041      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10042      * Used by Store query builder to append _requestMeta to params.
10043      * 
10044      */
10045     metaFromRemote : false,
10046     /**
10047      * This method is only used by a DataProxy which has retrieved data from a remote server.
10048      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10049      * @return {Object} data A data block which is used by an Roo.data.Store object as
10050      * a cache of Roo.data.Records.
10051      */
10052     read : function(response){
10053         var json = response.responseText;
10054        
10055         var o = /* eval:var:o */ eval("("+json+")");
10056         if(!o) {
10057             throw {message: "JsonReader.read: Json object not found"};
10058         }
10059         
10060         if(o.metaData){
10061             
10062             delete this.ef;
10063             this.metaFromRemote = true;
10064             this.meta = o.metaData;
10065             this.recordType = Roo.data.Record.create(o.metaData.fields);
10066             this.onMetaChange(this.meta, this.recordType, o);
10067         }
10068         return this.readRecords(o);
10069     },
10070
10071     // private function a store will implement
10072     onMetaChange : function(meta, recordType, o){
10073
10074     },
10075
10076     /**
10077          * @ignore
10078          */
10079     simpleAccess: function(obj, subsc) {
10080         return obj[subsc];
10081     },
10082
10083         /**
10084          * @ignore
10085          */
10086     getJsonAccessor: function(){
10087         var re = /[\[\.]/;
10088         return function(expr) {
10089             try {
10090                 return(re.test(expr))
10091                     ? new Function("obj", "return obj." + expr)
10092                     : function(obj){
10093                         return obj[expr];
10094                     };
10095             } catch(e){}
10096             return Roo.emptyFn;
10097         };
10098     }(),
10099
10100     /**
10101      * Create a data block containing Roo.data.Records from an XML document.
10102      * @param {Object} o An object which contains an Array of row objects in the property specified
10103      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10104      * which contains the total size of the dataset.
10105      * @return {Object} data A data block which is used by an Roo.data.Store object as
10106      * a cache of Roo.data.Records.
10107      */
10108     readRecords : function(o){
10109         /**
10110          * After any data loads, the raw JSON data is available for further custom processing.
10111          * @type Object
10112          */
10113         this.o = o;
10114         var s = this.meta, Record = this.recordType,
10115             f = Record.prototype.fields, fi = f.items, fl = f.length;
10116
10117 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10118         if (!this.ef) {
10119             if(s.totalProperty) {
10120                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10121                 }
10122                 if(s.successProperty) {
10123                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10124                 }
10125                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10126                 if (s.id) {
10127                         var g = this.getJsonAccessor(s.id);
10128                         this.getId = function(rec) {
10129                                 var r = g(rec);
10130                                 return (r === undefined || r === "") ? null : r;
10131                         };
10132                 } else {
10133                         this.getId = function(){return null;};
10134                 }
10135             this.ef = [];
10136             for(var jj = 0; jj < fl; jj++){
10137                 f = fi[jj];
10138                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10139                 this.ef[jj] = this.getJsonAccessor(map);
10140             }
10141         }
10142
10143         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10144         if(s.totalProperty){
10145             var vt = parseInt(this.getTotal(o), 10);
10146             if(!isNaN(vt)){
10147                 totalRecords = vt;
10148             }
10149         }
10150         if(s.successProperty){
10151             var vs = this.getSuccess(o);
10152             if(vs === false || vs === 'false'){
10153                 success = false;
10154             }
10155         }
10156         var records = [];
10157             for(var i = 0; i < c; i++){
10158                     var n = root[i];
10159                 var values = {};
10160                 var id = this.getId(n);
10161                 for(var j = 0; j < fl; j++){
10162                     f = fi[j];
10163                 var v = this.ef[j](n);
10164                 if (!f.convert) {
10165                     Roo.log('missing convert for ' + f.name);
10166                     Roo.log(f);
10167                     continue;
10168                 }
10169                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10170                 }
10171                 var record = new Record(values, id);
10172                 record.json = n;
10173                 records[i] = record;
10174             }
10175             return {
10176             raw : o,
10177                 success : success,
10178                 records : records,
10179                 totalRecords : totalRecords
10180             };
10181     }
10182 });/*
10183  * Based on:
10184  * Ext JS Library 1.1.1
10185  * Copyright(c) 2006-2007, Ext JS, LLC.
10186  *
10187  * Originally Released Under LGPL - original licence link has changed is not relivant.
10188  *
10189  * Fork - LGPL
10190  * <script type="text/javascript">
10191  */
10192
10193 /**
10194  * @class Roo.data.ArrayReader
10195  * @extends Roo.data.DataReader
10196  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10197  * Each element of that Array represents a row of data fields. The
10198  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10199  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10200  * <p>
10201  * Example code:.
10202  * <pre><code>
10203 var RecordDef = Roo.data.Record.create([
10204     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10205     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10206 ]);
10207 var myReader = new Roo.data.ArrayReader({
10208     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10209 }, RecordDef);
10210 </code></pre>
10211  * <p>
10212  * This would consume an Array like this:
10213  * <pre><code>
10214 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10215   </code></pre>
10216  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10217  * @constructor
10218  * Create a new JsonReader
10219  * @param {Object} meta Metadata configuration options.
10220  * @param {Object} recordType Either an Array of field definition objects
10221  * as specified to {@link Roo.data.Record#create},
10222  * or an {@link Roo.data.Record} object
10223  * created using {@link Roo.data.Record#create}.
10224  */
10225 Roo.data.ArrayReader = function(meta, recordType){
10226     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10227 };
10228
10229 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10230     /**
10231      * Create a data block containing Roo.data.Records from an XML document.
10232      * @param {Object} o An Array of row objects which represents the dataset.
10233      * @return {Object} data A data block which is used by an Roo.data.Store object as
10234      * a cache of Roo.data.Records.
10235      */
10236     readRecords : function(o){
10237         var sid = this.meta ? this.meta.id : null;
10238         var recordType = this.recordType, fields = recordType.prototype.fields;
10239         var records = [];
10240         var root = o;
10241             for(var i = 0; i < root.length; i++){
10242                     var n = root[i];
10243                 var values = {};
10244                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10245                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10246                 var f = fields.items[j];
10247                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10248                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10249                 v = f.convert(v);
10250                 values[f.name] = v;
10251             }
10252                 var record = new recordType(values, id);
10253                 record.json = n;
10254                 records[records.length] = record;
10255             }
10256             return {
10257                 records : records,
10258                 totalRecords : records.length
10259             };
10260     }
10261 });/*
10262  * - LGPL
10263  * * 
10264  */
10265
10266 /**
10267  * @class Roo.bootstrap.ComboBox
10268  * @extends Roo.bootstrap.TriggerField
10269  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10270  * @cfg {Boolean} append (true|false) default false
10271  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10272  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10273  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10274  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10275  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10276  * @constructor
10277  * Create a new ComboBox.
10278  * @param {Object} config Configuration options
10279  */
10280 Roo.bootstrap.ComboBox = function(config){
10281     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10282     this.addEvents({
10283         /**
10284          * @event expand
10285          * Fires when the dropdown list is expanded
10286              * @param {Roo.bootstrap.ComboBox} combo This combo box
10287              */
10288         'expand' : true,
10289         /**
10290          * @event collapse
10291          * Fires when the dropdown list is collapsed
10292              * @param {Roo.bootstrap.ComboBox} combo This combo box
10293              */
10294         'collapse' : true,
10295         /**
10296          * @event beforeselect
10297          * Fires before a list item is selected. Return false to cancel the selection.
10298              * @param {Roo.bootstrap.ComboBox} combo This combo box
10299              * @param {Roo.data.Record} record The data record returned from the underlying store
10300              * @param {Number} index The index of the selected item in the dropdown list
10301              */
10302         'beforeselect' : true,
10303         /**
10304          * @event select
10305          * Fires when a list item is selected
10306              * @param {Roo.bootstrap.ComboBox} combo This combo box
10307              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10308              * @param {Number} index The index of the selected item in the dropdown list
10309              */
10310         'select' : true,
10311         /**
10312          * @event beforequery
10313          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10314          * The event object passed has these properties:
10315              * @param {Roo.bootstrap.ComboBox} combo This combo box
10316              * @param {String} query The query
10317              * @param {Boolean} forceAll true to force "all" query
10318              * @param {Boolean} cancel true to cancel the query
10319              * @param {Object} e The query event object
10320              */
10321         'beforequery': true,
10322          /**
10323          * @event add
10324          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10325              * @param {Roo.bootstrap.ComboBox} combo This combo box
10326              */
10327         'add' : true,
10328         /**
10329          * @event edit
10330          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10331              * @param {Roo.bootstrap.ComboBox} combo This combo box
10332              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10333              */
10334         'edit' : true,
10335         /**
10336          * @event remove
10337          * Fires when the remove value from the combobox array
10338              * @param {Roo.bootstrap.ComboBox} combo This combo box
10339              */
10340         'remove' : true
10341         
10342     });
10343     
10344     this.item = [];
10345     this.tickItems = [];
10346     
10347     this.selectedIndex = -1;
10348     if(this.mode == 'local'){
10349         if(config.queryDelay === undefined){
10350             this.queryDelay = 10;
10351         }
10352         if(config.minChars === undefined){
10353             this.minChars = 0;
10354         }
10355     }
10356 };
10357
10358 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10359      
10360     /**
10361      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10362      * rendering into an Roo.Editor, defaults to false)
10363      */
10364     /**
10365      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10366      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10367      */
10368     /**
10369      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10370      */
10371     /**
10372      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10373      * the dropdown list (defaults to undefined, with no header element)
10374      */
10375
10376      /**
10377      * @cfg {String/Roo.Template} tpl The template to use to render the output
10378      */
10379      
10380      /**
10381      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10382      */
10383     listWidth: undefined,
10384     /**
10385      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10386      * mode = 'remote' or 'text' if mode = 'local')
10387      */
10388     displayField: undefined,
10389     /**
10390      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10391      * mode = 'remote' or 'value' if mode = 'local'). 
10392      * Note: use of a valueField requires the user make a selection
10393      * in order for a value to be mapped.
10394      */
10395     valueField: undefined,
10396     
10397     
10398     /**
10399      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10400      * field's data value (defaults to the underlying DOM element's name)
10401      */
10402     hiddenName: undefined,
10403     /**
10404      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10405      */
10406     listClass: '',
10407     /**
10408      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10409      */
10410     selectedClass: 'active',
10411     
10412     /**
10413      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10414      */
10415     shadow:'sides',
10416     /**
10417      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10418      * anchor positions (defaults to 'tl-bl')
10419      */
10420     listAlign: 'tl-bl?',
10421     /**
10422      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10423      */
10424     maxHeight: 300,
10425     /**
10426      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10427      * query specified by the allQuery config option (defaults to 'query')
10428      */
10429     triggerAction: 'query',
10430     /**
10431      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10432      * (defaults to 4, does not apply if editable = false)
10433      */
10434     minChars : 4,
10435     /**
10436      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10437      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10438      */
10439     typeAhead: false,
10440     /**
10441      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10442      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10443      */
10444     queryDelay: 500,
10445     /**
10446      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10447      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10448      */
10449     pageSize: 0,
10450     /**
10451      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10452      * when editable = true (defaults to false)
10453      */
10454     selectOnFocus:false,
10455     /**
10456      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10457      */
10458     queryParam: 'query',
10459     /**
10460      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10461      * when mode = 'remote' (defaults to 'Loading...')
10462      */
10463     loadingText: 'Loading...',
10464     /**
10465      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10466      */
10467     resizable: false,
10468     /**
10469      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10470      */
10471     handleHeight : 8,
10472     /**
10473      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10474      * traditional select (defaults to true)
10475      */
10476     editable: true,
10477     /**
10478      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10479      */
10480     allQuery: '',
10481     /**
10482      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10483      */
10484     mode: 'remote',
10485     /**
10486      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10487      * listWidth has a higher value)
10488      */
10489     minListWidth : 70,
10490     /**
10491      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10492      * allow the user to set arbitrary text into the field (defaults to false)
10493      */
10494     forceSelection:false,
10495     /**
10496      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10497      * if typeAhead = true (defaults to 250)
10498      */
10499     typeAheadDelay : 250,
10500     /**
10501      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10502      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10503      */
10504     valueNotFoundText : undefined,
10505     /**
10506      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10507      */
10508     blockFocus : false,
10509     
10510     /**
10511      * @cfg {Boolean} disableClear Disable showing of clear button.
10512      */
10513     disableClear : false,
10514     /**
10515      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10516      */
10517     alwaysQuery : false,
10518     
10519     /**
10520      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10521      */
10522     multiple : false,
10523     
10524     //private
10525     addicon : false,
10526     editicon: false,
10527     
10528     page: 0,
10529     hasQuery: false,
10530     append: false,
10531     loadNext: false,
10532     autoFocus : true,
10533     tickable : false,
10534     btnPosition : 'right',
10535     triggerList : true,
10536     showToggleBtn : true,
10537     // element that contains real text value.. (when hidden is used..)
10538     
10539     getAutoCreate : function()
10540     {
10541         var cfg = false;
10542         
10543         /*
10544          *  Normal ComboBox
10545          */
10546         if(!this.tickable){
10547             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10548             return cfg;
10549         }
10550         
10551         /*
10552          *  ComboBox with tickable selections
10553          */
10554              
10555         var align = this.labelAlign || this.parentLabelAlign();
10556         
10557         cfg = {
10558             cls : 'form-group roo-combobox-tickable' //input-group
10559         };
10560         
10561         
10562         var buttons = {
10563             tag : 'div',
10564             cls : 'tickable-buttons',
10565             cn : [
10566                 {
10567                     tag : 'button',
10568                     type : 'button',
10569                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10570                     html : 'Edit'
10571                 },
10572                 {
10573                     tag : 'button',
10574                     type : 'button',
10575                     name : 'ok',
10576                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10577                     html : 'Done'
10578                 },
10579                 {
10580                     tag : 'button',
10581                     type : 'button',
10582                     name : 'cancel',
10583                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10584                     html : 'Cancel'
10585                 }
10586             ]
10587         };
10588         
10589         var _this = this;
10590         Roo.each(buttons.cn, function(c){
10591             if (_this.size) {
10592                 c.cls += ' btn-' + _this.size;
10593             }
10594
10595             if (_this.disabled) {
10596                 c.disabled = true;
10597             }
10598         });
10599         
10600         var box = {
10601             tag: 'div',
10602             cn: [
10603                 {
10604                     tag: 'input',
10605                     type : 'hidden',
10606                     cls: 'form-hidden-field'
10607                 },
10608                 {
10609                     tag: 'ul',
10610                     cls: 'select2-choices',
10611                     cn:[
10612                         {
10613                             tag: 'li',
10614                             cls: 'select2-search-field',
10615                             cn: [
10616
10617                                 buttons
10618                             ]
10619                         }
10620                     ]
10621                 }
10622             ]
10623         }
10624         
10625         var combobox = {
10626             cls: 'select2-container input-group select2-container-multi',
10627             cn: [
10628                 box
10629 //                {
10630 //                    tag: 'ul',
10631 //                    cls: 'typeahead typeahead-long dropdown-menu',
10632 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10633 //                }
10634             ]
10635         };
10636         
10637         if (align ==='left' && this.fieldLabel.length) {
10638             
10639                 Roo.log("left and has label");
10640                 cfg.cn = [
10641                     
10642                     {
10643                         tag: 'label',
10644                         'for' :  id,
10645                         cls : 'control-label col-sm-' + this.labelWidth,
10646                         html : this.fieldLabel
10647                         
10648                     },
10649                     {
10650                         cls : "col-sm-" + (12 - this.labelWidth), 
10651                         cn: [
10652                             combobox
10653                         ]
10654                     }
10655                     
10656                 ];
10657         } else if ( this.fieldLabel.length) {
10658                 Roo.log(" label");
10659                  cfg.cn = [
10660                    
10661                     {
10662                         tag: 'label',
10663                         //cls : 'input-group-addon',
10664                         html : this.fieldLabel
10665                         
10666                     },
10667                     
10668                     combobox
10669                     
10670                 ];
10671
10672         } else {
10673             
10674                 Roo.log(" no label && no align");
10675                 cfg = combobox
10676                      
10677                 
10678         }
10679          
10680         var settings=this;
10681         ['xs','sm','md','lg'].map(function(size){
10682             if (settings[size]) {
10683                 cfg.cls += ' col-' + size + '-' + settings[size];
10684             }
10685         });
10686         
10687         return cfg;
10688         
10689     },
10690     
10691     // private
10692     initEvents: function()
10693     {
10694         
10695         if (!this.store) {
10696             throw "can not find store for combo";
10697         }
10698         this.store = Roo.factory(this.store, Roo.data);
10699         
10700         if(this.tickable){
10701             this.initTickableEvents();
10702             return;
10703         }
10704         
10705         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10706         
10707         if(this.hiddenName){
10708             
10709             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10710             
10711             this.hiddenField.dom.value =
10712                 this.hiddenValue !== undefined ? this.hiddenValue :
10713                 this.value !== undefined ? this.value : '';
10714
10715             // prevent input submission
10716             this.el.dom.removeAttribute('name');
10717             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10718              
10719              
10720         }
10721         //if(Roo.isGecko){
10722         //    this.el.dom.setAttribute('autocomplete', 'off');
10723         //}
10724         
10725         var cls = 'x-combo-list';
10726         
10727         //this.list = new Roo.Layer({
10728         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10729         //});
10730         
10731         var _this = this;
10732         
10733         (function(){
10734             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10735             _this.list.setWidth(lw);
10736         }).defer(100);
10737         
10738         this.list.on('mouseover', this.onViewOver, this);
10739         this.list.on('mousemove', this.onViewMove, this);
10740         
10741         this.list.on('scroll', this.onViewScroll, this);
10742         
10743         /*
10744         this.list.swallowEvent('mousewheel');
10745         this.assetHeight = 0;
10746
10747         if(this.title){
10748             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10749             this.assetHeight += this.header.getHeight();
10750         }
10751
10752         this.innerList = this.list.createChild({cls:cls+'-inner'});
10753         this.innerList.on('mouseover', this.onViewOver, this);
10754         this.innerList.on('mousemove', this.onViewMove, this);
10755         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10756         
10757         if(this.allowBlank && !this.pageSize && !this.disableClear){
10758             this.footer = this.list.createChild({cls:cls+'-ft'});
10759             this.pageTb = new Roo.Toolbar(this.footer);
10760            
10761         }
10762         if(this.pageSize){
10763             this.footer = this.list.createChild({cls:cls+'-ft'});
10764             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10765                     {pageSize: this.pageSize});
10766             
10767         }
10768         
10769         if (this.pageTb && this.allowBlank && !this.disableClear) {
10770             var _this = this;
10771             this.pageTb.add(new Roo.Toolbar.Fill(), {
10772                 cls: 'x-btn-icon x-btn-clear',
10773                 text: '&#160;',
10774                 handler: function()
10775                 {
10776                     _this.collapse();
10777                     _this.clearValue();
10778                     _this.onSelect(false, -1);
10779                 }
10780             });
10781         }
10782         if (this.footer) {
10783             this.assetHeight += this.footer.getHeight();
10784         }
10785         */
10786             
10787         if(!this.tpl){
10788             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10789         }
10790
10791         this.view = new Roo.View(this.list, this.tpl, {
10792             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10793         });
10794         //this.view.wrapEl.setDisplayed(false);
10795         this.view.on('click', this.onViewClick, this);
10796         
10797         
10798         
10799         this.store.on('beforeload', this.onBeforeLoad, this);
10800         this.store.on('load', this.onLoad, this);
10801         this.store.on('loadexception', this.onLoadException, this);
10802         /*
10803         if(this.resizable){
10804             this.resizer = new Roo.Resizable(this.list,  {
10805                pinned:true, handles:'se'
10806             });
10807             this.resizer.on('resize', function(r, w, h){
10808                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10809                 this.listWidth = w;
10810                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10811                 this.restrictHeight();
10812             }, this);
10813             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10814         }
10815         */
10816         if(!this.editable){
10817             this.editable = true;
10818             this.setEditable(false);
10819         }
10820         
10821         /*
10822         
10823         if (typeof(this.events.add.listeners) != 'undefined') {
10824             
10825             this.addicon = this.wrap.createChild(
10826                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10827        
10828             this.addicon.on('click', function(e) {
10829                 this.fireEvent('add', this);
10830             }, this);
10831         }
10832         if (typeof(this.events.edit.listeners) != 'undefined') {
10833             
10834             this.editicon = this.wrap.createChild(
10835                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10836             if (this.addicon) {
10837                 this.editicon.setStyle('margin-left', '40px');
10838             }
10839             this.editicon.on('click', function(e) {
10840                 
10841                 // we fire even  if inothing is selected..
10842                 this.fireEvent('edit', this, this.lastData );
10843                 
10844             }, this);
10845         }
10846         */
10847         
10848         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10849             "up" : function(e){
10850                 this.inKeyMode = true;
10851                 this.selectPrev();
10852             },
10853
10854             "down" : function(e){
10855                 if(!this.isExpanded()){
10856                     this.onTriggerClick();
10857                 }else{
10858                     this.inKeyMode = true;
10859                     this.selectNext();
10860                 }
10861             },
10862
10863             "enter" : function(e){
10864 //                this.onViewClick();
10865                 //return true;
10866                 this.collapse();
10867                 
10868                 if(this.fireEvent("specialkey", this, e)){
10869                     this.onViewClick(false);
10870                 }
10871                 
10872                 return true;
10873             },
10874
10875             "esc" : function(e){
10876                 this.collapse();
10877             },
10878
10879             "tab" : function(e){
10880                 this.collapse();
10881                 
10882                 if(this.fireEvent("specialkey", this, e)){
10883                     this.onViewClick(false);
10884                 }
10885                 
10886                 return true;
10887             },
10888
10889             scope : this,
10890
10891             doRelay : function(foo, bar, hname){
10892                 if(hname == 'down' || this.scope.isExpanded()){
10893                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10894                 }
10895                 return true;
10896             },
10897
10898             forceKeyDown: true
10899         });
10900         
10901         
10902         this.queryDelay = Math.max(this.queryDelay || 10,
10903                 this.mode == 'local' ? 10 : 250);
10904         
10905         
10906         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10907         
10908         if(this.typeAhead){
10909             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10910         }
10911         if(this.editable !== false){
10912             this.inputEl().on("keyup", this.onKeyUp, this);
10913         }
10914         if(this.forceSelection){
10915             this.inputEl().on('blur', this.doForce, this);
10916         }
10917         
10918         if(this.multiple){
10919             this.choices = this.el.select('ul.select2-choices', true).first();
10920             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10921         }
10922     },
10923     
10924     initTickableEvents: function()
10925     {   
10926         this.createList();
10927         
10928         if(this.hiddenName){
10929             
10930             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10931             
10932             this.hiddenField.dom.value =
10933                 this.hiddenValue !== undefined ? this.hiddenValue :
10934                 this.value !== undefined ? this.value : '';
10935
10936             // prevent input submission
10937             this.el.dom.removeAttribute('name');
10938             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10939              
10940              
10941         }
10942         
10943 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10944         
10945         this.choices = this.el.select('ul.select2-choices', true).first();
10946         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10947         if(this.triggerList){
10948             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10949         }
10950          
10951         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10952         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10953         
10954         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10955         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10956         
10957         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10958         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10959         
10960         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10961         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10962         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10963         
10964         this.okBtn.hide();
10965         this.cancelBtn.hide();
10966         
10967         var _this = this;
10968         
10969         (function(){
10970             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10971             _this.list.setWidth(lw);
10972         }).defer(100);
10973         
10974         this.list.on('mouseover', this.onViewOver, this);
10975         this.list.on('mousemove', this.onViewMove, this);
10976         
10977         this.list.on('scroll', this.onViewScroll, this);
10978         
10979         if(!this.tpl){
10980             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
10981         }
10982
10983         this.view = new Roo.View(this.list, this.tpl, {
10984             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10985         });
10986         
10987         //this.view.wrapEl.setDisplayed(false);
10988         this.view.on('click', this.onViewClick, this);
10989         
10990         
10991         
10992         this.store.on('beforeload', this.onBeforeLoad, this);
10993         this.store.on('load', this.onLoad, this);
10994         this.store.on('loadexception', this.onLoadException, this);
10995         
10996 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10997 //            "up" : function(e){
10998 //                this.inKeyMode = true;
10999 //                this.selectPrev();
11000 //            },
11001 //
11002 //            "down" : function(e){
11003 //                if(!this.isExpanded()){
11004 //                    this.onTriggerClick();
11005 //                }else{
11006 //                    this.inKeyMode = true;
11007 //                    this.selectNext();
11008 //                }
11009 //            },
11010 //
11011 //            "enter" : function(e){
11012 ////                this.onViewClick();
11013 //                //return true;
11014 //                this.collapse();
11015 //                
11016 //                if(this.fireEvent("specialkey", this, e)){
11017 //                    this.onViewClick(false);
11018 //                }
11019 //                
11020 //                return true;
11021 //            },
11022 //
11023 //            "esc" : function(e){
11024 //                this.collapse();
11025 //            },
11026 //
11027 //            "tab" : function(e){
11028 //                this.collapse();
11029 //                
11030 //                if(this.fireEvent("specialkey", this, e)){
11031 //                    this.onViewClick(false);
11032 //                }
11033 //                
11034 //                return true;
11035 //            },
11036 //
11037 //            scope : this,
11038 //
11039 //            doRelay : function(foo, bar, hname){
11040 //                if(hname == 'down' || this.scope.isExpanded()){
11041 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11042 //                }
11043 //                return true;
11044 //            },
11045 //
11046 //            forceKeyDown: true
11047 //        });
11048         
11049         
11050         this.queryDelay = Math.max(this.queryDelay || 10,
11051                 this.mode == 'local' ? 10 : 250);
11052         
11053         
11054         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11055         
11056         if(this.typeAhead){
11057             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11058         }
11059     },
11060
11061     onDestroy : function(){
11062         if(this.view){
11063             this.view.setStore(null);
11064             this.view.el.removeAllListeners();
11065             this.view.el.remove();
11066             this.view.purgeListeners();
11067         }
11068         if(this.list){
11069             this.list.dom.innerHTML  = '';
11070         }
11071         
11072         if(this.store){
11073             this.store.un('beforeload', this.onBeforeLoad, this);
11074             this.store.un('load', this.onLoad, this);
11075             this.store.un('loadexception', this.onLoadException, this);
11076         }
11077         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11078     },
11079
11080     // private
11081     fireKey : function(e){
11082         if(e.isNavKeyPress() && !this.list.isVisible()){
11083             this.fireEvent("specialkey", this, e);
11084         }
11085     },
11086
11087     // private
11088     onResize: function(w, h){
11089 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11090 //        
11091 //        if(typeof w != 'number'){
11092 //            // we do not handle it!?!?
11093 //            return;
11094 //        }
11095 //        var tw = this.trigger.getWidth();
11096 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11097 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11098 //        var x = w - tw;
11099 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11100 //            
11101 //        //this.trigger.setStyle('left', x+'px');
11102 //        
11103 //        if(this.list && this.listWidth === undefined){
11104 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11105 //            this.list.setWidth(lw);
11106 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11107 //        }
11108         
11109     
11110         
11111     },
11112
11113     /**
11114      * Allow or prevent the user from directly editing the field text.  If false is passed,
11115      * the user will only be able to select from the items defined in the dropdown list.  This method
11116      * is the runtime equivalent of setting the 'editable' config option at config time.
11117      * @param {Boolean} value True to allow the user to directly edit the field text
11118      */
11119     setEditable : function(value){
11120         if(value == this.editable){
11121             return;
11122         }
11123         this.editable = value;
11124         if(!value){
11125             this.inputEl().dom.setAttribute('readOnly', true);
11126             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11127             this.inputEl().addClass('x-combo-noedit');
11128         }else{
11129             this.inputEl().dom.setAttribute('readOnly', false);
11130             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11131             this.inputEl().removeClass('x-combo-noedit');
11132         }
11133     },
11134
11135     // private
11136     
11137     onBeforeLoad : function(combo,opts){
11138         if(!this.hasFocus){
11139             return;
11140         }
11141          if (!opts.add) {
11142             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11143          }
11144         this.restrictHeight();
11145         this.selectedIndex = -1;
11146     },
11147
11148     // private
11149     onLoad : function(){
11150         
11151         this.hasQuery = false;
11152         
11153         if(!this.hasFocus){
11154             return;
11155         }
11156         
11157         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11158             this.loading.hide();
11159         }
11160         
11161         if(this.store.getCount() > 0){
11162             this.expand();
11163 //            this.restrictHeight();
11164             if(this.lastQuery == this.allQuery){
11165                 if(this.editable && !this.tickable){
11166                     this.inputEl().dom.select();
11167                 }
11168                 
11169                 if(
11170                     !this.selectByValue(this.value, true) &&
11171                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11172                     this.store.lastOptions.add != true)
11173                 ){
11174                     this.select(0, true);
11175                 }
11176             }else{
11177                 if(this.autoFocus){
11178                     this.selectNext();
11179                 }
11180                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11181                     this.taTask.delay(this.typeAheadDelay);
11182                 }
11183             }
11184         }else{
11185             this.onEmptyResults();
11186         }
11187         
11188         //this.el.focus();
11189     },
11190     // private
11191     onLoadException : function()
11192     {
11193         this.hasQuery = false;
11194         
11195         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11196             this.loading.hide();
11197         }
11198         
11199         this.collapse();
11200         Roo.log(this.store.reader.jsonData);
11201         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11202             // fixme
11203             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11204         }
11205         
11206         
11207     },
11208     // private
11209     onTypeAhead : function(){
11210         if(this.store.getCount() > 0){
11211             var r = this.store.getAt(0);
11212             var newValue = r.data[this.displayField];
11213             var len = newValue.length;
11214             var selStart = this.getRawValue().length;
11215             
11216             if(selStart != len){
11217                 this.setRawValue(newValue);
11218                 this.selectText(selStart, newValue.length);
11219             }
11220         }
11221     },
11222
11223     // private
11224     onSelect : function(record, index){
11225         
11226         if(this.fireEvent('beforeselect', this, record, index) !== false){
11227         
11228             this.setFromData(index > -1 ? record.data : false);
11229             
11230             this.collapse();
11231             this.fireEvent('select', this, record, index);
11232         }
11233     },
11234
11235     /**
11236      * Returns the currently selected field value or empty string if no value is set.
11237      * @return {String} value The selected value
11238      */
11239     getValue : function(){
11240         
11241         if(this.multiple){
11242             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11243         }
11244         
11245         if(this.valueField){
11246             return typeof this.value != 'undefined' ? this.value : '';
11247         }else{
11248             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11249         }
11250     },
11251
11252     /**
11253      * Clears any text/value currently set in the field
11254      */
11255     clearValue : function(){
11256         if(this.hiddenField){
11257             this.hiddenField.dom.value = '';
11258         }
11259         this.value = '';
11260         this.setRawValue('');
11261         this.lastSelectionText = '';
11262         
11263     },
11264
11265     /**
11266      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11267      * will be displayed in the field.  If the value does not match the data value of an existing item,
11268      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11269      * Otherwise the field will be blank (although the value will still be set).
11270      * @param {String} value The value to match
11271      */
11272     setValue : function(v){
11273         if(this.multiple){
11274             this.syncValue();
11275             return;
11276         }
11277         
11278         var text = v;
11279         if(this.valueField){
11280             var r = this.findRecord(this.valueField, v);
11281             if(r){
11282                 text = r.data[this.displayField];
11283             }else if(this.valueNotFoundText !== undefined){
11284                 text = this.valueNotFoundText;
11285             }
11286         }
11287         this.lastSelectionText = text;
11288         if(this.hiddenField){
11289             this.hiddenField.dom.value = v;
11290         }
11291         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11292         this.value = v;
11293     },
11294     /**
11295      * @property {Object} the last set data for the element
11296      */
11297     
11298     lastData : false,
11299     /**
11300      * Sets the value of the field based on a object which is related to the record format for the store.
11301      * @param {Object} value the value to set as. or false on reset?
11302      */
11303     setFromData : function(o){
11304         
11305         if(this.multiple){
11306             if(typeof o.display_name !== 'string'){
11307                 for(var i=0;i<o.display_name.length;i++){
11308                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11309                 }
11310                 return;
11311             }
11312             this.addItem(o);
11313             return;
11314         }
11315             
11316         var dv = ''; // display value
11317         var vv = ''; // value value..
11318         this.lastData = o;
11319         if (this.displayField) {
11320             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11321         } else {
11322             // this is an error condition!!!
11323             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11324         }
11325         
11326         if(this.valueField){
11327             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11328         }
11329         
11330         if(this.hiddenField){
11331             this.hiddenField.dom.value = vv;
11332             
11333             this.lastSelectionText = dv;
11334             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11335             this.value = vv;
11336             return;
11337         }
11338         // no hidden field.. - we store the value in 'value', but still display
11339         // display field!!!!
11340         this.lastSelectionText = dv;
11341         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11342         this.value = vv;
11343         
11344         
11345     },
11346     // private
11347     reset : function(){
11348         // overridden so that last data is reset..
11349         this.setValue(this.originalValue);
11350         this.clearInvalid();
11351         this.lastData = false;
11352         if (this.view) {
11353             this.view.clearSelections();
11354         }
11355     },
11356     // private
11357     findRecord : function(prop, value){
11358         var record;
11359         if(this.store.getCount() > 0){
11360             this.store.each(function(r){
11361                 if(r.data[prop] == value){
11362                     record = r;
11363                     return false;
11364                 }
11365                 return true;
11366             });
11367         }
11368         return record;
11369     },
11370     
11371     getName: function()
11372     {
11373         // returns hidden if it's set..
11374         if (!this.rendered) {return ''};
11375         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11376         
11377     },
11378     // private
11379     onViewMove : function(e, t){
11380         this.inKeyMode = false;
11381     },
11382
11383     // private
11384     onViewOver : function(e, t){
11385         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11386             return;
11387         }
11388         var item = this.view.findItemFromChild(t);
11389         
11390         if(item){
11391             var index = this.view.indexOf(item);
11392             this.select(index, false);
11393         }
11394     },
11395
11396     // private
11397     onViewClick : function(view, doFocus, el, e)
11398     {
11399         var index = this.view.getSelectedIndexes()[0];
11400         
11401         var r = this.store.getAt(index);
11402         
11403         if(this.tickable){
11404             
11405             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11406                 return;
11407             }
11408             
11409             var rm = false;
11410             var _this = this;
11411             
11412             Roo.each(this.tickItems, function(v,k){
11413                 
11414                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11415                     _this.tickItems.splice(k, 1);
11416                     rm = true;
11417                     return;
11418                 }
11419             })
11420             
11421             if(rm){
11422                 return;
11423             }
11424             
11425             this.tickItems.push(r.data);
11426             return;
11427         }
11428         
11429         if(r){
11430             this.onSelect(r, index);
11431         }
11432         if(doFocus !== false && !this.blockFocus){
11433             this.inputEl().focus();
11434         }
11435     },
11436
11437     // private
11438     restrictHeight : function(){
11439         //this.innerList.dom.style.height = '';
11440         //var inner = this.innerList.dom;
11441         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11442         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11443         //this.list.beginUpdate();
11444         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11445         this.list.alignTo(this.inputEl(), this.listAlign);
11446         this.list.alignTo(this.inputEl(), this.listAlign);
11447         //this.list.endUpdate();
11448     },
11449
11450     // private
11451     onEmptyResults : function(){
11452         this.collapse();
11453     },
11454
11455     /**
11456      * Returns true if the dropdown list is expanded, else false.
11457      */
11458     isExpanded : function(){
11459         return this.list.isVisible();
11460     },
11461
11462     /**
11463      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11464      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11465      * @param {String} value The data value of the item to select
11466      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11467      * selected item if it is not currently in view (defaults to true)
11468      * @return {Boolean} True if the value matched an item in the list, else false
11469      */
11470     selectByValue : function(v, scrollIntoView){
11471         if(v !== undefined && v !== null){
11472             var r = this.findRecord(this.valueField || this.displayField, v);
11473             if(r){
11474                 this.select(this.store.indexOf(r), scrollIntoView);
11475                 return true;
11476             }
11477         }
11478         return false;
11479     },
11480
11481     /**
11482      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11483      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11484      * @param {Number} index The zero-based index of the list item to select
11485      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11486      * selected item if it is not currently in view (defaults to true)
11487      */
11488     select : function(index, scrollIntoView){
11489         this.selectedIndex = index;
11490         this.view.select(index);
11491         if(scrollIntoView !== false){
11492             var el = this.view.getNode(index);
11493             if(el && !this.multiple && !this.tickable){
11494                 this.list.scrollChildIntoView(el, false);
11495             }
11496         }
11497     },
11498
11499     // private
11500     selectNext : function(){
11501         var ct = this.store.getCount();
11502         if(ct > 0){
11503             if(this.selectedIndex == -1){
11504                 this.select(0);
11505             }else if(this.selectedIndex < ct-1){
11506                 this.select(this.selectedIndex+1);
11507             }
11508         }
11509     },
11510
11511     // private
11512     selectPrev : function(){
11513         var ct = this.store.getCount();
11514         if(ct > 0){
11515             if(this.selectedIndex == -1){
11516                 this.select(0);
11517             }else if(this.selectedIndex != 0){
11518                 this.select(this.selectedIndex-1);
11519             }
11520         }
11521     },
11522
11523     // private
11524     onKeyUp : function(e){
11525         if(this.editable !== false && !e.isSpecialKey()){
11526             this.lastKey = e.getKey();
11527             this.dqTask.delay(this.queryDelay);
11528         }
11529     },
11530
11531     // private
11532     validateBlur : function(){
11533         return !this.list || !this.list.isVisible();   
11534     },
11535
11536     // private
11537     initQuery : function(){
11538         this.doQuery(this.getRawValue());
11539     },
11540
11541     // private
11542     doForce : function(){
11543         if(this.inputEl().dom.value.length > 0){
11544             this.inputEl().dom.value =
11545                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11546              
11547         }
11548     },
11549
11550     /**
11551      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11552      * query allowing the query action to be canceled if needed.
11553      * @param {String} query The SQL query to execute
11554      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11555      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11556      * saved in the current store (defaults to false)
11557      */
11558     doQuery : function(q, forceAll){
11559         
11560         if(q === undefined || q === null){
11561             q = '';
11562         }
11563         var qe = {
11564             query: q,
11565             forceAll: forceAll,
11566             combo: this,
11567             cancel:false
11568         };
11569         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11570             return false;
11571         }
11572         q = qe.query;
11573         
11574         forceAll = qe.forceAll;
11575         if(forceAll === true || (q.length >= this.minChars)){
11576             
11577             this.hasQuery = true;
11578             
11579             if(this.lastQuery != q || this.alwaysQuery){
11580                 this.lastQuery = q;
11581                 if(this.mode == 'local'){
11582                     this.selectedIndex = -1;
11583                     if(forceAll){
11584                         this.store.clearFilter();
11585                     }else{
11586                         this.store.filter(this.displayField, q);
11587                     }
11588                     this.onLoad();
11589                 }else{
11590                     this.store.baseParams[this.queryParam] = q;
11591                     
11592                     var options = {params : this.getParams(q)};
11593                     
11594                     if(this.loadNext){
11595                         options.add = true;
11596                         options.params.start = this.page * this.pageSize;
11597                     }
11598                     
11599                     this.store.load(options);
11600                     /*
11601                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11602                      *  we should expand the list on onLoad
11603                      *  so command out it
11604                      */
11605 //                    this.expand();
11606                 }
11607             }else{
11608                 this.selectedIndex = -1;
11609                 this.onLoad();   
11610             }
11611         }
11612         
11613         this.loadNext = false;
11614     },
11615
11616     // private
11617     getParams : function(q){
11618         var p = {};
11619         //p[this.queryParam] = q;
11620         
11621         if(this.pageSize){
11622             p.start = 0;
11623             p.limit = this.pageSize;
11624         }
11625         return p;
11626     },
11627
11628     /**
11629      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11630      */
11631     collapse : function(){
11632         if(!this.isExpanded()){
11633             return;
11634         }
11635         
11636         this.list.hide();
11637         
11638         if(this.tickable){
11639             this.okBtn.hide();
11640             this.cancelBtn.hide();
11641             this.trigger.show();
11642         }
11643         
11644         Roo.get(document).un('mousedown', this.collapseIf, this);
11645         Roo.get(document).un('mousewheel', this.collapseIf, this);
11646         if (!this.editable) {
11647             Roo.get(document).un('keydown', this.listKeyPress, this);
11648         }
11649         this.fireEvent('collapse', this);
11650     },
11651
11652     // private
11653     collapseIf : function(e){
11654         var in_combo  = e.within(this.el);
11655         var in_list =  e.within(this.list);
11656         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11657         
11658         if (in_combo || in_list || is_list) {
11659             //e.stopPropagation();
11660             return;
11661         }
11662         
11663         if(this.tickable){
11664             this.onTickableFooterButtonClick(e, false, false);
11665         }
11666
11667         this.collapse();
11668         
11669     },
11670
11671     /**
11672      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11673      */
11674     expand : function(){
11675        
11676         if(this.isExpanded() || !this.hasFocus){
11677             return;
11678         }
11679         
11680         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11681         this.list.setWidth(lw);
11682         
11683         
11684          Roo.log('expand');
11685         
11686         this.list.show();
11687         
11688         this.restrictHeight();
11689         
11690         if(this.tickable){
11691             
11692             this.tickItems = Roo.apply([], this.item);
11693             
11694             this.okBtn.show();
11695             this.cancelBtn.show();
11696             this.trigger.hide();
11697             
11698         }
11699         
11700         Roo.get(document).on('mousedown', this.collapseIf, this);
11701         Roo.get(document).on('mousewheel', this.collapseIf, this);
11702         if (!this.editable) {
11703             Roo.get(document).on('keydown', this.listKeyPress, this);
11704         }
11705         
11706         this.fireEvent('expand', this);
11707     },
11708
11709     // private
11710     // Implements the default empty TriggerField.onTriggerClick function
11711     onTriggerClick : function(e)
11712     {
11713         Roo.log('trigger click');
11714         
11715         if(this.disabled || !this.triggerList){
11716             return;
11717         }
11718         
11719         this.page = 0;
11720         this.loadNext = false;
11721         
11722         if(this.isExpanded()){
11723             this.collapse();
11724             if (!this.blockFocus) {
11725                 this.inputEl().focus();
11726             }
11727             
11728         }else {
11729             this.hasFocus = true;
11730             if(this.triggerAction == 'all') {
11731                 this.doQuery(this.allQuery, true);
11732             } else {
11733                 this.doQuery(this.getRawValue());
11734             }
11735             if (!this.blockFocus) {
11736                 this.inputEl().focus();
11737             }
11738         }
11739     },
11740     
11741     onTickableTriggerClick : function(e)
11742     {
11743         if(this.disabled){
11744             return;
11745         }
11746         
11747         this.page = 0;
11748         this.loadNext = false;
11749         this.hasFocus = true;
11750         
11751         if(this.triggerAction == 'all') {
11752             this.doQuery(this.allQuery, true);
11753         } else {
11754             this.doQuery(this.getRawValue());
11755         }
11756     },
11757     
11758     onSearchFieldClick : function(e)
11759     {
11760         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11761             return;
11762         }
11763         
11764         this.page = 0;
11765         this.loadNext = false;
11766         this.hasFocus = true;
11767         
11768         if(this.triggerAction == 'all') {
11769             this.doQuery(this.allQuery, true);
11770         } else {
11771             this.doQuery(this.getRawValue());
11772         }
11773     },
11774     
11775     listKeyPress : function(e)
11776     {
11777         //Roo.log('listkeypress');
11778         // scroll to first matching element based on key pres..
11779         if (e.isSpecialKey()) {
11780             return false;
11781         }
11782         var k = String.fromCharCode(e.getKey()).toUpperCase();
11783         //Roo.log(k);
11784         var match  = false;
11785         var csel = this.view.getSelectedNodes();
11786         var cselitem = false;
11787         if (csel.length) {
11788             var ix = this.view.indexOf(csel[0]);
11789             cselitem  = this.store.getAt(ix);
11790             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11791                 cselitem = false;
11792             }
11793             
11794         }
11795         
11796         this.store.each(function(v) { 
11797             if (cselitem) {
11798                 // start at existing selection.
11799                 if (cselitem.id == v.id) {
11800                     cselitem = false;
11801                 }
11802                 return true;
11803             }
11804                 
11805             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11806                 match = this.store.indexOf(v);
11807                 return false;
11808             }
11809             return true;
11810         }, this);
11811         
11812         if (match === false) {
11813             return true; // no more action?
11814         }
11815         // scroll to?
11816         this.view.select(match);
11817         var sn = Roo.get(this.view.getSelectedNodes()[0])
11818         //sn.scrollIntoView(sn.dom.parentNode, false);
11819     },
11820     
11821     onViewScroll : function(e, t){
11822         
11823         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){
11824             return;
11825         }
11826         
11827         this.hasQuery = true;
11828         
11829         this.loading = this.list.select('.loading', true).first();
11830         
11831         if(this.loading === null){
11832             this.list.createChild({
11833                 tag: 'div',
11834                 cls: 'loading select2-more-results select2-active',
11835                 html: 'Loading more results...'
11836             })
11837             
11838             this.loading = this.list.select('.loading', true).first();
11839             
11840             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11841             
11842             this.loading.hide();
11843         }
11844         
11845         this.loading.show();
11846         
11847         var _combo = this;
11848         
11849         this.page++;
11850         this.loadNext = true;
11851         
11852         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11853         
11854         return;
11855     },
11856     
11857     addItem : function(o)
11858     {   
11859         var dv = ''; // display value
11860         
11861         if (this.displayField) {
11862             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11863         } else {
11864             // this is an error condition!!!
11865             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11866         }
11867         
11868         if(!dv.length){
11869             return;
11870         }
11871         
11872         var choice = this.choices.createChild({
11873             tag: 'li',
11874             cls: 'select2-search-choice',
11875             cn: [
11876                 {
11877                     tag: 'div',
11878                     html: dv
11879                 },
11880                 {
11881                     tag: 'a',
11882                     href: '#',
11883                     cls: 'select2-search-choice-close',
11884                     tabindex: '-1'
11885                 }
11886             ]
11887             
11888         }, this.searchField);
11889         
11890         var close = choice.select('a.select2-search-choice-close', true).first()
11891         
11892         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11893         
11894         this.item.push(o);
11895         
11896         this.lastData = o;
11897         
11898         this.syncValue();
11899         
11900         this.inputEl().dom.value = '';
11901         
11902     },
11903     
11904     onRemoveItem : function(e, _self, o)
11905     {
11906         e.preventDefault();
11907         var index = this.item.indexOf(o.data) * 1;
11908         
11909         if( index < 0){
11910             Roo.log('not this item?!');
11911             return;
11912         }
11913         
11914         this.item.splice(index, 1);
11915         o.item.remove();
11916         
11917         this.syncValue();
11918         
11919         this.fireEvent('remove', this, e);
11920         
11921     },
11922     
11923     syncValue : function()
11924     {
11925         if(!this.item.length){
11926             this.clearValue();
11927             return;
11928         }
11929             
11930         var value = [];
11931         var _this = this;
11932         Roo.each(this.item, function(i){
11933             if(_this.valueField){
11934                 value.push(i[_this.valueField]);
11935                 return;
11936             }
11937
11938             value.push(i);
11939         });
11940
11941         this.value = value.join(',');
11942
11943         if(this.hiddenField){
11944             this.hiddenField.dom.value = this.value;
11945         }
11946     },
11947     
11948     clearItem : function()
11949     {
11950         if(!this.multiple){
11951             return;
11952         }
11953         
11954         this.item = [];
11955         
11956         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11957            c.remove();
11958         });
11959         
11960         this.syncValue();
11961     },
11962     
11963     inputEl: function ()
11964     {
11965         if(this.tickable){
11966             return this.searchField;
11967         }
11968         return this.el.select('input.form-control',true).first();
11969     },
11970     
11971     
11972     onTickableFooterButtonClick : function(e, btn, el)
11973     {
11974         e.preventDefault();
11975         
11976         if(btn && btn.name == 'cancel'){
11977             this.tickItems = Roo.apply([], this.item);
11978             this.collapse();
11979             return;
11980         }
11981         
11982         this.clearItem();
11983         
11984         var _this = this;
11985         
11986         Roo.each(this.tickItems, function(o){
11987             _this.addItem(o);
11988         });
11989         
11990         this.collapse();
11991         
11992     }
11993     
11994     
11995
11996     /** 
11997     * @cfg {Boolean} grow 
11998     * @hide 
11999     */
12000     /** 
12001     * @cfg {Number} growMin 
12002     * @hide 
12003     */
12004     /** 
12005     * @cfg {Number} growMax 
12006     * @hide 
12007     */
12008     /**
12009      * @hide
12010      * @method autoSize
12011      */
12012 });
12013 /*
12014  * Based on:
12015  * Ext JS Library 1.1.1
12016  * Copyright(c) 2006-2007, Ext JS, LLC.
12017  *
12018  * Originally Released Under LGPL - original licence link has changed is not relivant.
12019  *
12020  * Fork - LGPL
12021  * <script type="text/javascript">
12022  */
12023
12024 /**
12025  * @class Roo.View
12026  * @extends Roo.util.Observable
12027  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12028  * This class also supports single and multi selection modes. <br>
12029  * Create a data model bound view:
12030  <pre><code>
12031  var store = new Roo.data.Store(...);
12032
12033  var view = new Roo.View({
12034     el : "my-element",
12035     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12036  
12037     singleSelect: true,
12038     selectedClass: "ydataview-selected",
12039     store: store
12040  });
12041
12042  // listen for node click?
12043  view.on("click", function(vw, index, node, e){
12044  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12045  });
12046
12047  // load XML data
12048  dataModel.load("foobar.xml");
12049  </code></pre>
12050  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12051  * <br><br>
12052  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12053  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12054  * 
12055  * Note: old style constructor is still suported (container, template, config)
12056  * 
12057  * @constructor
12058  * Create a new View
12059  * @param {Object} config The config object
12060  * 
12061  */
12062 Roo.View = function(config, depreciated_tpl, depreciated_config){
12063     
12064     this.parent = false;
12065     
12066     if (typeof(depreciated_tpl) == 'undefined') {
12067         // new way.. - universal constructor.
12068         Roo.apply(this, config);
12069         this.el  = Roo.get(this.el);
12070     } else {
12071         // old format..
12072         this.el  = Roo.get(config);
12073         this.tpl = depreciated_tpl;
12074         Roo.apply(this, depreciated_config);
12075     }
12076     this.wrapEl  = this.el.wrap().wrap();
12077     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12078     
12079     
12080     if(typeof(this.tpl) == "string"){
12081         this.tpl = new Roo.Template(this.tpl);
12082     } else {
12083         // support xtype ctors..
12084         this.tpl = new Roo.factory(this.tpl, Roo);
12085     }
12086     
12087     
12088     this.tpl.compile();
12089     
12090     /** @private */
12091     this.addEvents({
12092         /**
12093          * @event beforeclick
12094          * Fires before a click is processed. Returns false to cancel the default action.
12095          * @param {Roo.View} this
12096          * @param {Number} index The index of the target node
12097          * @param {HTMLElement} node The target node
12098          * @param {Roo.EventObject} e The raw event object
12099          */
12100             "beforeclick" : true,
12101         /**
12102          * @event click
12103          * Fires when a template node is clicked.
12104          * @param {Roo.View} this
12105          * @param {Number} index The index of the target node
12106          * @param {HTMLElement} node The target node
12107          * @param {Roo.EventObject} e The raw event object
12108          */
12109             "click" : true,
12110         /**
12111          * @event dblclick
12112          * Fires when a template node is double clicked.
12113          * @param {Roo.View} this
12114          * @param {Number} index The index of the target node
12115          * @param {HTMLElement} node The target node
12116          * @param {Roo.EventObject} e The raw event object
12117          */
12118             "dblclick" : true,
12119         /**
12120          * @event contextmenu
12121          * Fires when a template node is right clicked.
12122          * @param {Roo.View} this
12123          * @param {Number} index The index of the target node
12124          * @param {HTMLElement} node The target node
12125          * @param {Roo.EventObject} e The raw event object
12126          */
12127             "contextmenu" : true,
12128         /**
12129          * @event selectionchange
12130          * Fires when the selected nodes change.
12131          * @param {Roo.View} this
12132          * @param {Array} selections Array of the selected nodes
12133          */
12134             "selectionchange" : true,
12135     
12136         /**
12137          * @event beforeselect
12138          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12139          * @param {Roo.View} this
12140          * @param {HTMLElement} node The node to be selected
12141          * @param {Array} selections Array of currently selected nodes
12142          */
12143             "beforeselect" : true,
12144         /**
12145          * @event preparedata
12146          * Fires on every row to render, to allow you to change the data.
12147          * @param {Roo.View} this
12148          * @param {Object} data to be rendered (change this)
12149          */
12150           "preparedata" : true
12151           
12152           
12153         });
12154
12155
12156
12157     this.el.on({
12158         "click": this.onClick,
12159         "dblclick": this.onDblClick,
12160         "contextmenu": this.onContextMenu,
12161         scope:this
12162     });
12163
12164     this.selections = [];
12165     this.nodes = [];
12166     this.cmp = new Roo.CompositeElementLite([]);
12167     if(this.store){
12168         this.store = Roo.factory(this.store, Roo.data);
12169         this.setStore(this.store, true);
12170     }
12171     
12172     if ( this.footer && this.footer.xtype) {
12173            
12174          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12175         
12176         this.footer.dataSource = this.store
12177         this.footer.container = fctr;
12178         this.footer = Roo.factory(this.footer, Roo);
12179         fctr.insertFirst(this.el);
12180         
12181         // this is a bit insane - as the paging toolbar seems to detach the el..
12182 //        dom.parentNode.parentNode.parentNode
12183          // they get detached?
12184     }
12185     
12186     
12187     Roo.View.superclass.constructor.call(this);
12188     
12189     
12190 };
12191
12192 Roo.extend(Roo.View, Roo.util.Observable, {
12193     
12194      /**
12195      * @cfg {Roo.data.Store} store Data store to load data from.
12196      */
12197     store : false,
12198     
12199     /**
12200      * @cfg {String|Roo.Element} el The container element.
12201      */
12202     el : '',
12203     
12204     /**
12205      * @cfg {String|Roo.Template} tpl The template used by this View 
12206      */
12207     tpl : false,
12208     /**
12209      * @cfg {String} dataName the named area of the template to use as the data area
12210      *                          Works with domtemplates roo-name="name"
12211      */
12212     dataName: false,
12213     /**
12214      * @cfg {String} selectedClass The css class to add to selected nodes
12215      */
12216     selectedClass : "x-view-selected",
12217      /**
12218      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12219      */
12220     emptyText : "",
12221     
12222     /**
12223      * @cfg {String} text to display on mask (default Loading)
12224      */
12225     mask : false,
12226     /**
12227      * @cfg {Boolean} multiSelect Allow multiple selection
12228      */
12229     multiSelect : false,
12230     /**
12231      * @cfg {Boolean} singleSelect Allow single selection
12232      */
12233     singleSelect:  false,
12234     
12235     /**
12236      * @cfg {Boolean} toggleSelect - selecting 
12237      */
12238     toggleSelect : false,
12239     
12240     /**
12241      * @cfg {Boolean} tickable - selecting 
12242      */
12243     tickable : false,
12244     
12245     /**
12246      * Returns the element this view is bound to.
12247      * @return {Roo.Element}
12248      */
12249     getEl : function(){
12250         return this.wrapEl;
12251     },
12252     
12253     
12254
12255     /**
12256      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12257      */
12258     refresh : function(){
12259         Roo.log('refresh');
12260         var t = this.tpl;
12261         
12262         // if we are using something like 'domtemplate', then
12263         // the what gets used is:
12264         // t.applySubtemplate(NAME, data, wrapping data..)
12265         // the outer template then get' applied with
12266         //     the store 'extra data'
12267         // and the body get's added to the
12268         //      roo-name="data" node?
12269         //      <span class='roo-tpl-{name}'></span> ?????
12270         
12271         
12272         
12273         this.clearSelections();
12274         this.el.update("");
12275         var html = [];
12276         var records = this.store.getRange();
12277         if(records.length < 1) {
12278             
12279             // is this valid??  = should it render a template??
12280             
12281             this.el.update(this.emptyText);
12282             return;
12283         }
12284         var el = this.el;
12285         if (this.dataName) {
12286             this.el.update(t.apply(this.store.meta)); //????
12287             el = this.el.child('.roo-tpl-' + this.dataName);
12288         }
12289         
12290         for(var i = 0, len = records.length; i < len; i++){
12291             var data = this.prepareData(records[i].data, i, records[i]);
12292             this.fireEvent("preparedata", this, data, i, records[i]);
12293             
12294             var d = Roo.apply({}, data);
12295             
12296             if(this.tickable){
12297                 Roo.apply(d, {'roo-id' : Roo.id()});
12298                 
12299                 var _this = this;
12300             
12301                 Roo.each(this.parent.item, function(item){
12302                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12303                         return;
12304                     }
12305                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12306                 });
12307             }
12308             
12309             html[html.length] = Roo.util.Format.trim(
12310                 this.dataName ?
12311                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12312                     t.apply(d)
12313             );
12314         }
12315         
12316         
12317         
12318         el.update(html.join(""));
12319         this.nodes = el.dom.childNodes;
12320         this.updateIndexes(0);
12321     },
12322     
12323
12324     /**
12325      * Function to override to reformat the data that is sent to
12326      * the template for each node.
12327      * DEPRICATED - use the preparedata event handler.
12328      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12329      * a JSON object for an UpdateManager bound view).
12330      */
12331     prepareData : function(data, index, record)
12332     {
12333         this.fireEvent("preparedata", this, data, index, record);
12334         return data;
12335     },
12336
12337     onUpdate : function(ds, record){
12338          Roo.log('on update');   
12339         this.clearSelections();
12340         var index = this.store.indexOf(record);
12341         var n = this.nodes[index];
12342         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12343         n.parentNode.removeChild(n);
12344         this.updateIndexes(index, index);
12345     },
12346
12347     
12348     
12349 // --------- FIXME     
12350     onAdd : function(ds, records, index)
12351     {
12352         Roo.log(['on Add', ds, records, index] );        
12353         this.clearSelections();
12354         if(this.nodes.length == 0){
12355             this.refresh();
12356             return;
12357         }
12358         var n = this.nodes[index];
12359         for(var i = 0, len = records.length; i < len; i++){
12360             var d = this.prepareData(records[i].data, i, records[i]);
12361             if(n){
12362                 this.tpl.insertBefore(n, d);
12363             }else{
12364                 
12365                 this.tpl.append(this.el, d);
12366             }
12367         }
12368         this.updateIndexes(index);
12369     },
12370
12371     onRemove : function(ds, record, index){
12372         Roo.log('onRemove');
12373         this.clearSelections();
12374         var el = this.dataName  ?
12375             this.el.child('.roo-tpl-' + this.dataName) :
12376             this.el; 
12377         
12378         el.dom.removeChild(this.nodes[index]);
12379         this.updateIndexes(index);
12380     },
12381
12382     /**
12383      * Refresh an individual node.
12384      * @param {Number} index
12385      */
12386     refreshNode : function(index){
12387         this.onUpdate(this.store, this.store.getAt(index));
12388     },
12389
12390     updateIndexes : function(startIndex, endIndex){
12391         var ns = this.nodes;
12392         startIndex = startIndex || 0;
12393         endIndex = endIndex || ns.length - 1;
12394         for(var i = startIndex; i <= endIndex; i++){
12395             ns[i].nodeIndex = i;
12396         }
12397     },
12398
12399     /**
12400      * Changes the data store this view uses and refresh the view.
12401      * @param {Store} store
12402      */
12403     setStore : function(store, initial){
12404         if(!initial && this.store){
12405             this.store.un("datachanged", this.refresh);
12406             this.store.un("add", this.onAdd);
12407             this.store.un("remove", this.onRemove);
12408             this.store.un("update", this.onUpdate);
12409             this.store.un("clear", this.refresh);
12410             this.store.un("beforeload", this.onBeforeLoad);
12411             this.store.un("load", this.onLoad);
12412             this.store.un("loadexception", this.onLoad);
12413         }
12414         if(store){
12415           
12416             store.on("datachanged", this.refresh, this);
12417             store.on("add", this.onAdd, this);
12418             store.on("remove", this.onRemove, this);
12419             store.on("update", this.onUpdate, this);
12420             store.on("clear", this.refresh, this);
12421             store.on("beforeload", this.onBeforeLoad, this);
12422             store.on("load", this.onLoad, this);
12423             store.on("loadexception", this.onLoad, this);
12424         }
12425         
12426         if(store){
12427             this.refresh();
12428         }
12429     },
12430     /**
12431      * onbeforeLoad - masks the loading area.
12432      *
12433      */
12434     onBeforeLoad : function(store,opts)
12435     {
12436          Roo.log('onBeforeLoad');   
12437         if (!opts.add) {
12438             this.el.update("");
12439         }
12440         this.el.mask(this.mask ? this.mask : "Loading" ); 
12441     },
12442     onLoad : function ()
12443     {
12444         this.el.unmask();
12445     },
12446     
12447
12448     /**
12449      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12450      * @param {HTMLElement} node
12451      * @return {HTMLElement} The template node
12452      */
12453     findItemFromChild : function(node){
12454         var el = this.dataName  ?
12455             this.el.child('.roo-tpl-' + this.dataName,true) :
12456             this.el.dom; 
12457         
12458         if(!node || node.parentNode == el){
12459                     return node;
12460             }
12461             var p = node.parentNode;
12462             while(p && p != el){
12463             if(p.parentNode == el){
12464                 return p;
12465             }
12466             p = p.parentNode;
12467         }
12468             return null;
12469     },
12470
12471     /** @ignore */
12472     onClick : function(e){
12473         var item = this.findItemFromChild(e.getTarget());
12474         if(item){
12475             var index = this.indexOf(item);
12476             if(this.onItemClick(item, index, e) !== false){
12477                 this.fireEvent("click", this, index, item, e);
12478             }
12479         }else{
12480             this.clearSelections();
12481         }
12482     },
12483
12484     /** @ignore */
12485     onContextMenu : function(e){
12486         var item = this.findItemFromChild(e.getTarget());
12487         if(item){
12488             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12489         }
12490     },
12491
12492     /** @ignore */
12493     onDblClick : function(e){
12494         var item = this.findItemFromChild(e.getTarget());
12495         if(item){
12496             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12497         }
12498     },
12499
12500     onItemClick : function(item, index, e)
12501     {
12502         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12503             return false;
12504         }
12505         if (this.toggleSelect) {
12506             var m = this.isSelected(item) ? 'unselect' : 'select';
12507             Roo.log(m);
12508             var _t = this;
12509             _t[m](item, true, false);
12510             return true;
12511         }
12512         if(this.multiSelect || this.singleSelect){
12513             if(this.multiSelect && e.shiftKey && this.lastSelection){
12514                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12515             }else{
12516                 this.select(item, this.multiSelect && e.ctrlKey);
12517                 this.lastSelection = item;
12518             }
12519             
12520             if(!this.tickable){
12521                 e.preventDefault();
12522             }
12523             
12524         }
12525         return true;
12526     },
12527
12528     /**
12529      * Get the number of selected nodes.
12530      * @return {Number}
12531      */
12532     getSelectionCount : function(){
12533         return this.selections.length;
12534     },
12535
12536     /**
12537      * Get the currently selected nodes.
12538      * @return {Array} An array of HTMLElements
12539      */
12540     getSelectedNodes : function(){
12541         return this.selections;
12542     },
12543
12544     /**
12545      * Get the indexes of the selected nodes.
12546      * @return {Array}
12547      */
12548     getSelectedIndexes : function(){
12549         var indexes = [], s = this.selections;
12550         for(var i = 0, len = s.length; i < len; i++){
12551             indexes.push(s[i].nodeIndex);
12552         }
12553         return indexes;
12554     },
12555
12556     /**
12557      * Clear all selections
12558      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12559      */
12560     clearSelections : function(suppressEvent){
12561         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12562             this.cmp.elements = this.selections;
12563             this.cmp.removeClass(this.selectedClass);
12564             this.selections = [];
12565             if(!suppressEvent){
12566                 this.fireEvent("selectionchange", this, this.selections);
12567             }
12568         }
12569     },
12570
12571     /**
12572      * Returns true if the passed node is selected
12573      * @param {HTMLElement/Number} node The node or node index
12574      * @return {Boolean}
12575      */
12576     isSelected : function(node){
12577         var s = this.selections;
12578         if(s.length < 1){
12579             return false;
12580         }
12581         node = this.getNode(node);
12582         return s.indexOf(node) !== -1;
12583     },
12584
12585     /**
12586      * Selects nodes.
12587      * @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
12588      * @param {Boolean} keepExisting (optional) true to keep existing selections
12589      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12590      */
12591     select : function(nodeInfo, keepExisting, suppressEvent){
12592         if(nodeInfo instanceof Array){
12593             if(!keepExisting){
12594                 this.clearSelections(true);
12595             }
12596             for(var i = 0, len = nodeInfo.length; i < len; i++){
12597                 this.select(nodeInfo[i], true, true);
12598             }
12599             return;
12600         } 
12601         var node = this.getNode(nodeInfo);
12602         if(!node || this.isSelected(node)){
12603             return; // already selected.
12604         }
12605         if(!keepExisting){
12606             this.clearSelections(true);
12607         }
12608         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12609             Roo.fly(node).addClass(this.selectedClass);
12610             this.selections.push(node);
12611             if(!suppressEvent){
12612                 this.fireEvent("selectionchange", this, this.selections);
12613             }
12614         }
12615         
12616         
12617     },
12618       /**
12619      * Unselects nodes.
12620      * @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
12621      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12623      */
12624     unselect : function(nodeInfo, keepExisting, suppressEvent)
12625     {
12626         if(nodeInfo instanceof Array){
12627             Roo.each(this.selections, function(s) {
12628                 this.unselect(s, nodeInfo);
12629             }, this);
12630             return;
12631         }
12632         var node = this.getNode(nodeInfo);
12633         if(!node || !this.isSelected(node)){
12634             Roo.log("not selected");
12635             return; // not selected.
12636         }
12637         // fireevent???
12638         var ns = [];
12639         Roo.each(this.selections, function(s) {
12640             if (s == node ) {
12641                 Roo.fly(node).removeClass(this.selectedClass);
12642
12643                 return;
12644             }
12645             ns.push(s);
12646         },this);
12647         
12648         this.selections= ns;
12649         this.fireEvent("selectionchange", this, this.selections);
12650     },
12651
12652     /**
12653      * Gets a template node.
12654      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12655      * @return {HTMLElement} The node or null if it wasn't found
12656      */
12657     getNode : function(nodeInfo){
12658         if(typeof nodeInfo == "string"){
12659             return document.getElementById(nodeInfo);
12660         }else if(typeof nodeInfo == "number"){
12661             return this.nodes[nodeInfo];
12662         }
12663         return nodeInfo;
12664     },
12665
12666     /**
12667      * Gets a range template nodes.
12668      * @param {Number} startIndex
12669      * @param {Number} endIndex
12670      * @return {Array} An array of nodes
12671      */
12672     getNodes : function(start, end){
12673         var ns = this.nodes;
12674         start = start || 0;
12675         end = typeof end == "undefined" ? ns.length - 1 : end;
12676         var nodes = [];
12677         if(start <= end){
12678             for(var i = start; i <= end; i++){
12679                 nodes.push(ns[i]);
12680             }
12681         } else{
12682             for(var i = start; i >= end; i--){
12683                 nodes.push(ns[i]);
12684             }
12685         }
12686         return nodes;
12687     },
12688
12689     /**
12690      * Finds the index of the passed node
12691      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12692      * @return {Number} The index of the node or -1
12693      */
12694     indexOf : function(node){
12695         node = this.getNode(node);
12696         if(typeof node.nodeIndex == "number"){
12697             return node.nodeIndex;
12698         }
12699         var ns = this.nodes;
12700         for(var i = 0, len = ns.length; i < len; i++){
12701             if(ns[i] == node){
12702                 return i;
12703             }
12704         }
12705         return -1;
12706     }
12707 });
12708 /*
12709  * - LGPL
12710  *
12711  * based on jquery fullcalendar
12712  * 
12713  */
12714
12715 Roo.bootstrap = Roo.bootstrap || {};
12716 /**
12717  * @class Roo.bootstrap.Calendar
12718  * @extends Roo.bootstrap.Component
12719  * Bootstrap Calendar class
12720  * @cfg {Boolean} loadMask (true|false) default false
12721  * @cfg {Object} header generate the user specific header of the calendar, default false
12722
12723  * @constructor
12724  * Create a new Container
12725  * @param {Object} config The config object
12726  */
12727
12728
12729
12730 Roo.bootstrap.Calendar = function(config){
12731     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12732      this.addEvents({
12733         /**
12734              * @event select
12735              * Fires when a date is selected
12736              * @param {DatePicker} this
12737              * @param {Date} date The selected date
12738              */
12739         'select': true,
12740         /**
12741              * @event monthchange
12742              * Fires when the displayed month changes 
12743              * @param {DatePicker} this
12744              * @param {Date} date The selected month
12745              */
12746         'monthchange': true,
12747         /**
12748              * @event evententer
12749              * Fires when mouse over an event
12750              * @param {Calendar} this
12751              * @param {event} Event
12752              */
12753         'evententer': true,
12754         /**
12755              * @event eventleave
12756              * Fires when the mouse leaves an
12757              * @param {Calendar} this
12758              * @param {event}
12759              */
12760         'eventleave': true,
12761         /**
12762              * @event eventclick
12763              * Fires when the mouse click an
12764              * @param {Calendar} this
12765              * @param {event}
12766              */
12767         'eventclick': true
12768         
12769     });
12770
12771 };
12772
12773 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12774     
12775      /**
12776      * @cfg {Number} startDay
12777      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12778      */
12779     startDay : 0,
12780     
12781     loadMask : false,
12782     
12783     header : false,
12784       
12785     getAutoCreate : function(){
12786         
12787         
12788         var fc_button = function(name, corner, style, content ) {
12789             return Roo.apply({},{
12790                 tag : 'span',
12791                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12792                          (corner.length ?
12793                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12794                             ''
12795                         ),
12796                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12797                 unselectable: 'on'
12798             });
12799         };
12800         
12801         var header = {};
12802         
12803         if(!this.header){
12804             header = {
12805                 tag : 'table',
12806                 cls : 'fc-header',
12807                 style : 'width:100%',
12808                 cn : [
12809                     {
12810                         tag: 'tr',
12811                         cn : [
12812                             {
12813                                 tag : 'td',
12814                                 cls : 'fc-header-left',
12815                                 cn : [
12816                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12817                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12818                                     { tag: 'span', cls: 'fc-header-space' },
12819                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12820
12821
12822                                 ]
12823                             },
12824
12825                             {
12826                                 tag : 'td',
12827                                 cls : 'fc-header-center',
12828                                 cn : [
12829                                     {
12830                                         tag: 'span',
12831                                         cls: 'fc-header-title',
12832                                         cn : {
12833                                             tag: 'H2',
12834                                             html : 'month / year'
12835                                         }
12836                                     }
12837
12838                                 ]
12839                             },
12840                             {
12841                                 tag : 'td',
12842                                 cls : 'fc-header-right',
12843                                 cn : [
12844                               /*      fc_button('month', 'left', '', 'month' ),
12845                                     fc_button('week', '', '', 'week' ),
12846                                     fc_button('day', 'right', '', 'day' )
12847                                 */    
12848
12849                                 ]
12850                             }
12851
12852                         ]
12853                     }
12854                 ]
12855             };
12856         }
12857         
12858         header = this.header;
12859         
12860        
12861         var cal_heads = function() {
12862             var ret = [];
12863             // fixme - handle this.
12864             
12865             for (var i =0; i < Date.dayNames.length; i++) {
12866                 var d = Date.dayNames[i];
12867                 ret.push({
12868                     tag: 'th',
12869                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12870                     html : d.substring(0,3)
12871                 });
12872                 
12873             }
12874             ret[0].cls += ' fc-first';
12875             ret[6].cls += ' fc-last';
12876             return ret;
12877         };
12878         var cal_cell = function(n) {
12879             return  {
12880                 tag: 'td',
12881                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12882                 cn : [
12883                     {
12884                         cn : [
12885                             {
12886                                 cls: 'fc-day-number',
12887                                 html: 'D'
12888                             },
12889                             {
12890                                 cls: 'fc-day-content',
12891                              
12892                                 cn : [
12893                                      {
12894                                         style: 'position: relative;' // height: 17px;
12895                                     }
12896                                 ]
12897                             }
12898                             
12899                             
12900                         ]
12901                     }
12902                 ]
12903                 
12904             }
12905         };
12906         var cal_rows = function() {
12907             
12908             var ret = []
12909             for (var r = 0; r < 6; r++) {
12910                 var row= {
12911                     tag : 'tr',
12912                     cls : 'fc-week',
12913                     cn : []
12914                 };
12915                 
12916                 for (var i =0; i < Date.dayNames.length; i++) {
12917                     var d = Date.dayNames[i];
12918                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12919
12920                 }
12921                 row.cn[0].cls+=' fc-first';
12922                 row.cn[0].cn[0].style = 'min-height:90px';
12923                 row.cn[6].cls+=' fc-last';
12924                 ret.push(row);
12925                 
12926             }
12927             ret[0].cls += ' fc-first';
12928             ret[4].cls += ' fc-prev-last';
12929             ret[5].cls += ' fc-last';
12930             return ret;
12931             
12932         };
12933         
12934         var cal_table = {
12935             tag: 'table',
12936             cls: 'fc-border-separate',
12937             style : 'width:100%',
12938             cellspacing  : 0,
12939             cn : [
12940                 { 
12941                     tag: 'thead',
12942                     cn : [
12943                         { 
12944                             tag: 'tr',
12945                             cls : 'fc-first fc-last',
12946                             cn : cal_heads()
12947                         }
12948                     ]
12949                 },
12950                 { 
12951                     tag: 'tbody',
12952                     cn : cal_rows()
12953                 }
12954                   
12955             ]
12956         };
12957          
12958          var cfg = {
12959             cls : 'fc fc-ltr',
12960             cn : [
12961                 header,
12962                 {
12963                     cls : 'fc-content',
12964                     style : "position: relative;",
12965                     cn : [
12966                         {
12967                             cls : 'fc-view fc-view-month fc-grid',
12968                             style : 'position: relative',
12969                             unselectable : 'on',
12970                             cn : [
12971                                 {
12972                                     cls : 'fc-event-container',
12973                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12974                                 },
12975                                 cal_table
12976                             ]
12977                         }
12978                     ]
12979     
12980                 }
12981            ] 
12982             
12983         };
12984         
12985          
12986         
12987         return cfg;
12988     },
12989     
12990     
12991     initEvents : function()
12992     {
12993         if(!this.store){
12994             throw "can not find store for calendar";
12995         }
12996         
12997         var mark = {
12998             tag: "div",
12999             cls:"x-dlg-mask",
13000             style: "text-align:center",
13001             cn: [
13002                 {
13003                     tag: "div",
13004                     style: "background-color:white;width:50%;margin:250 auto",
13005                     cn: [
13006                         {
13007                             tag: "img",
13008                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13009                         },
13010                         {
13011                             tag: "span",
13012                             html: "Loading"
13013                         }
13014                         
13015                     ]
13016                 }
13017             ]
13018         }
13019         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13020         
13021         var size = this.el.select('.fc-content', true).first().getSize();
13022         this.maskEl.setSize(size.width, size.height);
13023         this.maskEl.enableDisplayMode("block");
13024         if(!this.loadMask){
13025             this.maskEl.hide();
13026         }
13027         
13028         this.store = Roo.factory(this.store, Roo.data);
13029         this.store.on('load', this.onLoad, this);
13030         this.store.on('beforeload', this.onBeforeLoad, this);
13031         
13032         this.resize();
13033         
13034         this.cells = this.el.select('.fc-day',true);
13035         //Roo.log(this.cells);
13036         this.textNodes = this.el.query('.fc-day-number');
13037         this.cells.addClassOnOver('fc-state-hover');
13038         
13039         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13040         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13041         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13042         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13043         
13044         this.on('monthchange', this.onMonthChange, this);
13045         
13046         this.update(new Date().clearTime());
13047     },
13048     
13049     resize : function() {
13050         var sz  = this.el.getSize();
13051         
13052         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13053         this.el.select('.fc-day-content div',true).setHeight(34);
13054     },
13055     
13056     
13057     // private
13058     showPrevMonth : function(e){
13059         this.update(this.activeDate.add("mo", -1));
13060     },
13061     showToday : function(e){
13062         this.update(new Date().clearTime());
13063     },
13064     // private
13065     showNextMonth : function(e){
13066         this.update(this.activeDate.add("mo", 1));
13067     },
13068
13069     // private
13070     showPrevYear : function(){
13071         this.update(this.activeDate.add("y", -1));
13072     },
13073
13074     // private
13075     showNextYear : function(){
13076         this.update(this.activeDate.add("y", 1));
13077     },
13078
13079     
13080    // private
13081     update : function(date)
13082     {
13083         var vd = this.activeDate;
13084         this.activeDate = date;
13085 //        if(vd && this.el){
13086 //            var t = date.getTime();
13087 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13088 //                Roo.log('using add remove');
13089 //                
13090 //                this.fireEvent('monthchange', this, date);
13091 //                
13092 //                this.cells.removeClass("fc-state-highlight");
13093 //                this.cells.each(function(c){
13094 //                   if(c.dateValue == t){
13095 //                       c.addClass("fc-state-highlight");
13096 //                       setTimeout(function(){
13097 //                            try{c.dom.firstChild.focus();}catch(e){}
13098 //                       }, 50);
13099 //                       return false;
13100 //                   }
13101 //                   return true;
13102 //                });
13103 //                return;
13104 //            }
13105 //        }
13106         
13107         var days = date.getDaysInMonth();
13108         
13109         var firstOfMonth = date.getFirstDateOfMonth();
13110         var startingPos = firstOfMonth.getDay()-this.startDay;
13111         
13112         if(startingPos < this.startDay){
13113             startingPos += 7;
13114         }
13115         
13116         var pm = date.add(Date.MONTH, -1);
13117         var prevStart = pm.getDaysInMonth()-startingPos;
13118 //        
13119         this.cells = this.el.select('.fc-day',true);
13120         this.textNodes = this.el.query('.fc-day-number');
13121         this.cells.addClassOnOver('fc-state-hover');
13122         
13123         var cells = this.cells.elements;
13124         var textEls = this.textNodes;
13125         
13126         Roo.each(cells, function(cell){
13127             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13128         });
13129         
13130         days += startingPos;
13131
13132         // convert everything to numbers so it's fast
13133         var day = 86400000;
13134         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13135         //Roo.log(d);
13136         //Roo.log(pm);
13137         //Roo.log(prevStart);
13138         
13139         var today = new Date().clearTime().getTime();
13140         var sel = date.clearTime().getTime();
13141         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13142         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13143         var ddMatch = this.disabledDatesRE;
13144         var ddText = this.disabledDatesText;
13145         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13146         var ddaysText = this.disabledDaysText;
13147         var format = this.format;
13148         
13149         var setCellClass = function(cal, cell){
13150             cell.row = 0;
13151             cell.events = [];
13152             cell.more = [];
13153             //Roo.log('set Cell Class');
13154             cell.title = "";
13155             var t = d.getTime();
13156             
13157             //Roo.log(d);
13158             
13159             cell.dateValue = t;
13160             if(t == today){
13161                 cell.className += " fc-today";
13162                 cell.className += " fc-state-highlight";
13163                 cell.title = cal.todayText;
13164             }
13165             if(t == sel){
13166                 // disable highlight in other month..
13167                 //cell.className += " fc-state-highlight";
13168                 
13169             }
13170             // disabling
13171             if(t < min) {
13172                 cell.className = " fc-state-disabled";
13173                 cell.title = cal.minText;
13174                 return;
13175             }
13176             if(t > max) {
13177                 cell.className = " fc-state-disabled";
13178                 cell.title = cal.maxText;
13179                 return;
13180             }
13181             if(ddays){
13182                 if(ddays.indexOf(d.getDay()) != -1){
13183                     cell.title = ddaysText;
13184                     cell.className = " fc-state-disabled";
13185                 }
13186             }
13187             if(ddMatch && format){
13188                 var fvalue = d.dateFormat(format);
13189                 if(ddMatch.test(fvalue)){
13190                     cell.title = ddText.replace("%0", fvalue);
13191                     cell.className = " fc-state-disabled";
13192                 }
13193             }
13194             
13195             if (!cell.initialClassName) {
13196                 cell.initialClassName = cell.dom.className;
13197             }
13198             
13199             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13200         };
13201
13202         var i = 0;
13203         
13204         for(; i < startingPos; i++) {
13205             textEls[i].innerHTML = (++prevStart);
13206             d.setDate(d.getDate()+1);
13207             
13208             cells[i].className = "fc-past fc-other-month";
13209             setCellClass(this, cells[i]);
13210         }
13211         
13212         var intDay = 0;
13213         
13214         for(; i < days; i++){
13215             intDay = i - startingPos + 1;
13216             textEls[i].innerHTML = (intDay);
13217             d.setDate(d.getDate()+1);
13218             
13219             cells[i].className = ''; // "x-date-active";
13220             setCellClass(this, cells[i]);
13221         }
13222         var extraDays = 0;
13223         
13224         for(; i < 42; i++) {
13225             textEls[i].innerHTML = (++extraDays);
13226             d.setDate(d.getDate()+1);
13227             
13228             cells[i].className = "fc-future fc-other-month";
13229             setCellClass(this, cells[i]);
13230         }
13231         
13232         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13233         
13234         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13235         
13236         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13237         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13238         
13239         if(totalRows != 6){
13240             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13241             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13242         }
13243         
13244         this.fireEvent('monthchange', this, date);
13245         
13246         
13247         /*
13248         if(!this.internalRender){
13249             var main = this.el.dom.firstChild;
13250             var w = main.offsetWidth;
13251             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13252             Roo.fly(main).setWidth(w);
13253             this.internalRender = true;
13254             // opera does not respect the auto grow header center column
13255             // then, after it gets a width opera refuses to recalculate
13256             // without a second pass
13257             if(Roo.isOpera && !this.secondPass){
13258                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13259                 this.secondPass = true;
13260                 this.update.defer(10, this, [date]);
13261             }
13262         }
13263         */
13264         
13265     },
13266     
13267     findCell : function(dt) {
13268         dt = dt.clearTime().getTime();
13269         var ret = false;
13270         this.cells.each(function(c){
13271             //Roo.log("check " +c.dateValue + '?=' + dt);
13272             if(c.dateValue == dt){
13273                 ret = c;
13274                 return false;
13275             }
13276             return true;
13277         });
13278         
13279         return ret;
13280     },
13281     
13282     findCells : function(ev) {
13283         var s = ev.start.clone().clearTime().getTime();
13284        // Roo.log(s);
13285         var e= ev.end.clone().clearTime().getTime();
13286        // Roo.log(e);
13287         var ret = [];
13288         this.cells.each(function(c){
13289              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13290             
13291             if(c.dateValue > e){
13292                 return ;
13293             }
13294             if(c.dateValue < s){
13295                 return ;
13296             }
13297             ret.push(c);
13298         });
13299         
13300         return ret;    
13301     },
13302     
13303 //    findBestRow: function(cells)
13304 //    {
13305 //        var ret = 0;
13306 //        
13307 //        for (var i =0 ; i < cells.length;i++) {
13308 //            ret  = Math.max(cells[i].rows || 0,ret);
13309 //        }
13310 //        return ret;
13311 //        
13312 //    },
13313     
13314     
13315     addItem : function(ev)
13316     {
13317         // look for vertical location slot in
13318         var cells = this.findCells(ev);
13319         
13320 //        ev.row = this.findBestRow(cells);
13321         
13322         // work out the location.
13323         
13324         var crow = false;
13325         var rows = [];
13326         for(var i =0; i < cells.length; i++) {
13327             
13328             cells[i].row = cells[0].row;
13329             
13330             if(i == 0){
13331                 cells[i].row = cells[i].row + 1;
13332             }
13333             
13334             if (!crow) {
13335                 crow = {
13336                     start : cells[i],
13337                     end :  cells[i]
13338                 };
13339                 continue;
13340             }
13341             if (crow.start.getY() == cells[i].getY()) {
13342                 // on same row.
13343                 crow.end = cells[i];
13344                 continue;
13345             }
13346             // different row.
13347             rows.push(crow);
13348             crow = {
13349                 start: cells[i],
13350                 end : cells[i]
13351             };
13352             
13353         }
13354         
13355         rows.push(crow);
13356         ev.els = [];
13357         ev.rows = rows;
13358         ev.cells = cells;
13359         
13360         cells[0].events.push(ev);
13361         
13362         this.calevents.push(ev);
13363     },
13364     
13365     clearEvents: function() {
13366         
13367         if(!this.calevents){
13368             return;
13369         }
13370         
13371         Roo.each(this.cells.elements, function(c){
13372             c.row = 0;
13373             c.events = [];
13374             c.more = [];
13375         });
13376         
13377         Roo.each(this.calevents, function(e) {
13378             Roo.each(e.els, function(el) {
13379                 el.un('mouseenter' ,this.onEventEnter, this);
13380                 el.un('mouseleave' ,this.onEventLeave, this);
13381                 el.remove();
13382             },this);
13383         },this);
13384         
13385         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13386             e.remove();
13387         });
13388         
13389     },
13390     
13391     renderEvents: function()
13392     {   
13393         var _this = this;
13394         
13395         this.cells.each(function(c) {
13396             
13397             if(c.row < 5){
13398                 return;
13399             }
13400             
13401             var ev = c.events;
13402             
13403             var r = 4;
13404             if(c.row != c.events.length){
13405                 r = 4 - (4 - (c.row - c.events.length));
13406             }
13407             
13408             c.events = ev.slice(0, r);
13409             c.more = ev.slice(r);
13410             
13411             if(c.more.length && c.more.length == 1){
13412                 c.events.push(c.more.pop());
13413             }
13414             
13415             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13416             
13417         });
13418             
13419         this.cells.each(function(c) {
13420             
13421             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13422             
13423             
13424             for (var e = 0; e < c.events.length; e++){
13425                 var ev = c.events[e];
13426                 var rows = ev.rows;
13427                 
13428                 for(var i = 0; i < rows.length; i++) {
13429                 
13430                     // how many rows should it span..
13431
13432                     var  cfg = {
13433                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13434                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13435
13436                         unselectable : "on",
13437                         cn : [
13438                             {
13439                                 cls: 'fc-event-inner',
13440                                 cn : [
13441     //                                {
13442     //                                  tag:'span',
13443     //                                  cls: 'fc-event-time',
13444     //                                  html : cells.length > 1 ? '' : ev.time
13445     //                                },
13446                                     {
13447                                       tag:'span',
13448                                       cls: 'fc-event-title',
13449                                       html : String.format('{0}', ev.title)
13450                                     }
13451
13452
13453                                 ]
13454                             },
13455                             {
13456                                 cls: 'ui-resizable-handle ui-resizable-e',
13457                                 html : '&nbsp;&nbsp;&nbsp'
13458                             }
13459
13460                         ]
13461                     };
13462
13463                     if (i == 0) {
13464                         cfg.cls += ' fc-event-start';
13465                     }
13466                     if ((i+1) == rows.length) {
13467                         cfg.cls += ' fc-event-end';
13468                     }
13469
13470                     var ctr = _this.el.select('.fc-event-container',true).first();
13471                     var cg = ctr.createChild(cfg);
13472
13473                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13474                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13475
13476                     var r = (c.more.length) ? 1 : 0;
13477                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13478                     cg.setWidth(ebox.right - sbox.x -2);
13479
13480                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13481                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13482                     cg.on('click', _this.onEventClick, _this, ev);
13483
13484                     ev.els.push(cg);
13485                     
13486                 }
13487                 
13488             }
13489             
13490             
13491             if(c.more.length){
13492                 var  cfg = {
13493                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13494                     style : 'position: absolute',
13495                     unselectable : "on",
13496                     cn : [
13497                         {
13498                             cls: 'fc-event-inner',
13499                             cn : [
13500                                 {
13501                                   tag:'span',
13502                                   cls: 'fc-event-title',
13503                                   html : 'More'
13504                                 }
13505
13506
13507                             ]
13508                         },
13509                         {
13510                             cls: 'ui-resizable-handle ui-resizable-e',
13511                             html : '&nbsp;&nbsp;&nbsp'
13512                         }
13513
13514                     ]
13515                 };
13516
13517                 var ctr = _this.el.select('.fc-event-container',true).first();
13518                 var cg = ctr.createChild(cfg);
13519
13520                 var sbox = c.select('.fc-day-content',true).first().getBox();
13521                 var ebox = c.select('.fc-day-content',true).first().getBox();
13522                 //Roo.log(cg);
13523                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13524                 cg.setWidth(ebox.right - sbox.x -2);
13525
13526                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13527                 
13528             }
13529             
13530         });
13531         
13532         
13533         
13534     },
13535     
13536     onEventEnter: function (e, el,event,d) {
13537         this.fireEvent('evententer', this, el, event);
13538     },
13539     
13540     onEventLeave: function (e, el,event,d) {
13541         this.fireEvent('eventleave', this, el, event);
13542     },
13543     
13544     onEventClick: function (e, el,event,d) {
13545         this.fireEvent('eventclick', this, el, event);
13546     },
13547     
13548     onMonthChange: function () {
13549         this.store.load();
13550     },
13551     
13552     onMoreEventClick: function(e, el, more)
13553     {
13554         var _this = this;
13555         
13556         this.calpopover.placement = 'right';
13557         this.calpopover.setTitle('More');
13558         
13559         this.calpopover.setContent('');
13560         
13561         var ctr = this.calpopover.el.select('.popover-content', true).first();
13562         
13563         Roo.each(more, function(m){
13564             var cfg = {
13565                 cls : 'fc-event-hori fc-event-draggable',
13566                 html : m.title
13567             }
13568             var cg = ctr.createChild(cfg);
13569             
13570             cg.on('click', _this.onEventClick, _this, m);
13571         });
13572         
13573         this.calpopover.show(el);
13574         
13575         
13576     },
13577     
13578     onLoad: function () 
13579     {   
13580         this.calevents = [];
13581         var cal = this;
13582         
13583         if(this.store.getCount() > 0){
13584             this.store.data.each(function(d){
13585                cal.addItem({
13586                     id : d.data.id,
13587                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13588                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13589                     time : d.data.start_time,
13590                     title : d.data.title,
13591                     description : d.data.description,
13592                     venue : d.data.venue
13593                 });
13594             });
13595         }
13596         
13597         this.renderEvents();
13598         
13599         if(this.calevents.length && this.loadMask){
13600             this.maskEl.hide();
13601         }
13602     },
13603     
13604     onBeforeLoad: function()
13605     {
13606         this.clearEvents();
13607         if(this.loadMask){
13608             this.maskEl.show();
13609         }
13610     }
13611 });
13612
13613  
13614  /*
13615  * - LGPL
13616  *
13617  * element
13618  * 
13619  */
13620
13621 /**
13622  * @class Roo.bootstrap.Popover
13623  * @extends Roo.bootstrap.Component
13624  * Bootstrap Popover class
13625  * @cfg {String} html contents of the popover   (or false to use children..)
13626  * @cfg {String} title of popover (or false to hide)
13627  * @cfg {String} placement how it is placed
13628  * @cfg {String} trigger click || hover (or false to trigger manually)
13629  * @cfg {String} over what (parent or false to trigger manually.)
13630  * 
13631  * @constructor
13632  * Create a new Popover
13633  * @param {Object} config The config object
13634  */
13635
13636 Roo.bootstrap.Popover = function(config){
13637     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13638 };
13639
13640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13641     
13642     title: 'Fill in a title',
13643     html: false,
13644     
13645     placement : 'right',
13646     trigger : 'hover', // hover
13647     
13648     over: 'parent',
13649     
13650     can_build_overlaid : false,
13651     
13652     getChildContainer : function()
13653     {
13654         return this.el.select('.popover-content',true).first();
13655     },
13656     
13657     getAutoCreate : function(){
13658          Roo.log('make popover?');
13659         var cfg = {
13660            cls : 'popover roo-dynamic',
13661            style: 'display:block',
13662            cn : [
13663                 {
13664                     cls : 'arrow'
13665                 },
13666                 {
13667                     cls : 'popover-inner',
13668                     cn : [
13669                         {
13670                             tag: 'h3',
13671                             cls: 'popover-title',
13672                             html : this.title
13673                         },
13674                         {
13675                             cls : 'popover-content',
13676                             html : this.html
13677                         }
13678                     ]
13679                     
13680                 }
13681            ]
13682         };
13683         
13684         return cfg;
13685     },
13686     setTitle: function(str)
13687     {
13688         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13689     },
13690     setContent: function(str)
13691     {
13692         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13693     },
13694     // as it get's added to the bottom of the page.
13695     onRender : function(ct, position)
13696     {
13697         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13698         if(!this.el){
13699             var cfg = Roo.apply({},  this.getAutoCreate());
13700             cfg.id = Roo.id();
13701             
13702             if (this.cls) {
13703                 cfg.cls += ' ' + this.cls;
13704             }
13705             if (this.style) {
13706                 cfg.style = this.style;
13707             }
13708             Roo.log("adding to ")
13709             this.el = Roo.get(document.body).createChild(cfg, position);
13710             Roo.log(this.el);
13711         }
13712         this.initEvents();
13713     },
13714     
13715     initEvents : function()
13716     {
13717         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13718         this.el.enableDisplayMode('block');
13719         this.el.hide();
13720         if (this.over === false) {
13721             return; 
13722         }
13723         if (this.triggers === false) {
13724             return;
13725         }
13726         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13727         var triggers = this.trigger ? this.trigger.split(' ') : [];
13728         Roo.each(triggers, function(trigger) {
13729         
13730             if (trigger == 'click') {
13731                 on_el.on('click', this.toggle, this);
13732             } else if (trigger != 'manual') {
13733                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13734                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13735       
13736                 on_el.on(eventIn  ,this.enter, this);
13737                 on_el.on(eventOut, this.leave, this);
13738             }
13739         }, this);
13740         
13741     },
13742     
13743     
13744     // private
13745     timeout : null,
13746     hoverState : null,
13747     
13748     toggle : function () {
13749         this.hoverState == 'in' ? this.leave() : this.enter();
13750     },
13751     
13752     enter : function () {
13753        
13754     
13755         clearTimeout(this.timeout);
13756     
13757         this.hoverState = 'in'
13758     
13759         if (!this.delay || !this.delay.show) {
13760             this.show();
13761             return 
13762         }
13763         var _t = this;
13764         this.timeout = setTimeout(function () {
13765             if (_t.hoverState == 'in') {
13766                 _t.show();
13767             }
13768         }, this.delay.show)
13769     },
13770     leave : function() {
13771         clearTimeout(this.timeout);
13772     
13773         this.hoverState = 'out'
13774     
13775         if (!this.delay || !this.delay.hide) {
13776             this.hide();
13777             return 
13778         }
13779         var _t = this;
13780         this.timeout = setTimeout(function () {
13781             if (_t.hoverState == 'out') {
13782                 _t.hide();
13783             }
13784         }, this.delay.hide)
13785     },
13786     
13787     show : function (on_el)
13788     {
13789         if (!on_el) {
13790             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13791         }
13792         // set content.
13793         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13794         if (this.html !== false) {
13795             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13796         }
13797         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13798         if (!this.title.length) {
13799             this.el.select('.popover-title',true).hide();
13800         }
13801         
13802         var placement = typeof this.placement == 'function' ?
13803             this.placement.call(this, this.el, on_el) :
13804             this.placement;
13805             
13806         var autoToken = /\s?auto?\s?/i;
13807         var autoPlace = autoToken.test(placement);
13808         if (autoPlace) {
13809             placement = placement.replace(autoToken, '') || 'top';
13810         }
13811         
13812         //this.el.detach()
13813         //this.el.setXY([0,0]);
13814         this.el.show();
13815         this.el.dom.style.display='block';
13816         this.el.addClass(placement);
13817         
13818         //this.el.appendTo(on_el);
13819         
13820         var p = this.getPosition();
13821         var box = this.el.getBox();
13822         
13823         if (autoPlace) {
13824             // fixme..
13825         }
13826         var align = Roo.bootstrap.Popover.alignment[placement]
13827         this.el.alignTo(on_el, align[0],align[1]);
13828         //var arrow = this.el.select('.arrow',true).first();
13829         //arrow.set(align[2], 
13830         
13831         this.el.addClass('in');
13832         this.hoverState = null;
13833         
13834         if (this.el.hasClass('fade')) {
13835             // fade it?
13836         }
13837         
13838     },
13839     hide : function()
13840     {
13841         this.el.setXY([0,0]);
13842         this.el.removeClass('in');
13843         this.el.hide();
13844         
13845     }
13846     
13847 });
13848
13849 Roo.bootstrap.Popover.alignment = {
13850     'left' : ['r-l', [-10,0], 'right'],
13851     'right' : ['l-r', [10,0], 'left'],
13852     'bottom' : ['t-b', [0,10], 'top'],
13853     'top' : [ 'b-t', [0,-10], 'bottom']
13854 };
13855
13856  /*
13857  * - LGPL
13858  *
13859  * Progress
13860  * 
13861  */
13862
13863 /**
13864  * @class Roo.bootstrap.Progress
13865  * @extends Roo.bootstrap.Component
13866  * Bootstrap Progress class
13867  * @cfg {Boolean} striped striped of the progress bar
13868  * @cfg {Boolean} active animated of the progress bar
13869  * 
13870  * 
13871  * @constructor
13872  * Create a new Progress
13873  * @param {Object} config The config object
13874  */
13875
13876 Roo.bootstrap.Progress = function(config){
13877     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13878 };
13879
13880 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13881     
13882     striped : false,
13883     active: false,
13884     
13885     getAutoCreate : function(){
13886         var cfg = {
13887             tag: 'div',
13888             cls: 'progress'
13889         };
13890         
13891         
13892         if(this.striped){
13893             cfg.cls += ' progress-striped';
13894         }
13895       
13896         if(this.active){
13897             cfg.cls += ' active';
13898         }
13899         
13900         
13901         return cfg;
13902     }
13903    
13904 });
13905
13906  
13907
13908  /*
13909  * - LGPL
13910  *
13911  * ProgressBar
13912  * 
13913  */
13914
13915 /**
13916  * @class Roo.bootstrap.ProgressBar
13917  * @extends Roo.bootstrap.Component
13918  * Bootstrap ProgressBar class
13919  * @cfg {Number} aria_valuenow aria-value now
13920  * @cfg {Number} aria_valuemin aria-value min
13921  * @cfg {Number} aria_valuemax aria-value max
13922  * @cfg {String} label label for the progress bar
13923  * @cfg {String} panel (success | info | warning | danger )
13924  * @cfg {String} role role of the progress bar
13925  * @cfg {String} sr_only text
13926  * 
13927  * 
13928  * @constructor
13929  * Create a new ProgressBar
13930  * @param {Object} config The config object
13931  */
13932
13933 Roo.bootstrap.ProgressBar = function(config){
13934     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13935 };
13936
13937 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13938     
13939     aria_valuenow : 0,
13940     aria_valuemin : 0,
13941     aria_valuemax : 100,
13942     label : false,
13943     panel : false,
13944     role : false,
13945     sr_only: false,
13946     
13947     getAutoCreate : function()
13948     {
13949         
13950         var cfg = {
13951             tag: 'div',
13952             cls: 'progress-bar',
13953             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13954         };
13955         
13956         if(this.sr_only){
13957             cfg.cn = {
13958                 tag: 'span',
13959                 cls: 'sr-only',
13960                 html: this.sr_only
13961             }
13962         }
13963         
13964         if(this.role){
13965             cfg.role = this.role;
13966         }
13967         
13968         if(this.aria_valuenow){
13969             cfg['aria-valuenow'] = this.aria_valuenow;
13970         }
13971         
13972         if(this.aria_valuemin){
13973             cfg['aria-valuemin'] = this.aria_valuemin;
13974         }
13975         
13976         if(this.aria_valuemax){
13977             cfg['aria-valuemax'] = this.aria_valuemax;
13978         }
13979         
13980         if(this.label && !this.sr_only){
13981             cfg.html = this.label;
13982         }
13983         
13984         if(this.panel){
13985             cfg.cls += ' progress-bar-' + this.panel;
13986         }
13987         
13988         return cfg;
13989     },
13990     
13991     update : function(aria_valuenow)
13992     {
13993         this.aria_valuenow = aria_valuenow;
13994         
13995         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13996     }
13997    
13998 });
13999
14000  
14001
14002  /*
14003  * - LGPL
14004  *
14005  * column
14006  * 
14007  */
14008
14009 /**
14010  * @class Roo.bootstrap.TabGroup
14011  * @extends Roo.bootstrap.Column
14012  * Bootstrap Column class
14013  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14014  * @cfg {Boolean} carousel true to make the group behave like a carousel
14015  * 
14016  * @constructor
14017  * Create a new TabGroup
14018  * @param {Object} config The config object
14019  */
14020
14021 Roo.bootstrap.TabGroup = function(config){
14022     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14023     if (!this.navId) {
14024         this.navId = Roo.id();
14025     }
14026     this.tabs = [];
14027     Roo.bootstrap.TabGroup.register(this);
14028     
14029 };
14030
14031 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14032     
14033     carousel : false,
14034     transition : false,
14035      
14036     getAutoCreate : function()
14037     {
14038         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14039         
14040         cfg.cls += ' tab-content';
14041         
14042         if (this.carousel) {
14043             cfg.cls += ' carousel slide';
14044             cfg.cn = [{
14045                cls : 'carousel-inner'
14046             }]
14047         }
14048         
14049         
14050         return cfg;
14051     },
14052     getChildContainer : function()
14053     {
14054         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14055     },
14056     
14057     /**
14058     * register a Navigation item
14059     * @param {Roo.bootstrap.NavItem} the navitem to add
14060     */
14061     register : function(item)
14062     {
14063         this.tabs.push( item);
14064         item.navId = this.navId; // not really needed..
14065     
14066     },
14067     
14068     getActivePanel : function()
14069     {
14070         var r = false;
14071         Roo.each(this.tabs, function(t) {
14072             if (t.active) {
14073                 r = t;
14074                 return false;
14075             }
14076             return null;
14077         });
14078         return r;
14079         
14080     },
14081     getPanelByName : function(n)
14082     {
14083         var r = false;
14084         Roo.each(this.tabs, function(t) {
14085             if (t.tabId == n) {
14086                 r = t;
14087                 return false;
14088             }
14089             return null;
14090         });
14091         return r;
14092     },
14093     indexOfPanel : function(p)
14094     {
14095         var r = false;
14096         Roo.each(this.tabs, function(t,i) {
14097             if (t.tabId == p.tabId) {
14098                 r = i;
14099                 return false;
14100             }
14101             return null;
14102         });
14103         return r;
14104     },
14105     /**
14106      * show a specific panel
14107      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14108      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14109      */
14110     showPanel : function (pan)
14111     {
14112         
14113         if (typeof(pan) == 'number') {
14114             pan = this.tabs[pan];
14115         }
14116         if (typeof(pan) == 'string') {
14117             pan = this.getPanelByName(pan);
14118         }
14119         if (pan.tabId == this.getActivePanel().tabId) {
14120             return true;
14121         }
14122         var cur = this.getActivePanel();
14123         
14124         if (false === cur.fireEvent('beforedeactivate')) {
14125             return false;
14126         }
14127         
14128         if (this.carousel) {
14129             this.transition = true;
14130             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14131             var lr = dir == 'next' ? 'left' : 'right';
14132             pan.el.addClass(dir); // or prev
14133             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14134             cur.el.addClass(lr); // or right
14135             pan.el.addClass(lr);
14136             
14137             var _this = this;
14138             cur.el.on('transitionend', function() {
14139                 Roo.log("trans end?");
14140                 
14141                 pan.el.removeClass([lr,dir]);
14142                 pan.setActive(true);
14143                 
14144                 cur.el.removeClass([lr]);
14145                 cur.setActive(false);
14146                 
14147                 _this.transition = false;
14148                 
14149             }, this, { single:  true } );
14150             return true;
14151         }
14152         
14153         cur.setActive(false);
14154         pan.setActive(true);
14155         return true;
14156         
14157     },
14158     showPanelNext : function()
14159     {
14160         var i = this.indexOfPanel(this.getActivePanel());
14161         if (i > this.tabs.length) {
14162             return;
14163         }
14164         this.showPanel(this.tabs[i+1]);
14165     },
14166     showPanelPrev : function()
14167     {
14168         var i = this.indexOfPanel(this.getActivePanel());
14169         if (i  < 1) {
14170             return;
14171         }
14172         this.showPanel(this.tabs[i-1]);
14173     }
14174     
14175     
14176   
14177 });
14178
14179  
14180
14181  
14182  
14183 Roo.apply(Roo.bootstrap.TabGroup, {
14184     
14185     groups: {},
14186      /**
14187     * register a Navigation Group
14188     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14189     */
14190     register : function(navgrp)
14191     {
14192         this.groups[navgrp.navId] = navgrp;
14193         
14194     },
14195     /**
14196     * fetch a Navigation Group based on the navigation ID
14197     * if one does not exist , it will get created.
14198     * @param {string} the navgroup to add
14199     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14200     */
14201     get: function(navId) {
14202         if (typeof(this.groups[navId]) == 'undefined') {
14203             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14204         }
14205         return this.groups[navId] ;
14206     }
14207     
14208     
14209     
14210 });
14211
14212  /*
14213  * - LGPL
14214  *
14215  * TabPanel
14216  * 
14217  */
14218
14219 /**
14220  * @class Roo.bootstrap.TabPanel
14221  * @extends Roo.bootstrap.Component
14222  * Bootstrap TabPanel class
14223  * @cfg {Boolean} active panel active
14224  * @cfg {String} html panel content
14225  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14226  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14227  * 
14228  * 
14229  * @constructor
14230  * Create a new TabPanel
14231  * @param {Object} config The config object
14232  */
14233
14234 Roo.bootstrap.TabPanel = function(config){
14235     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14236     this.addEvents({
14237         /**
14238              * @event changed
14239              * Fires when the active status changes
14240              * @param {Roo.bootstrap.TabPanel} this
14241              * @param {Boolean} state the new state
14242             
14243          */
14244         'changed': true,
14245         /**
14246              * @event beforedeactivate
14247              * Fires before a tab is de-activated - can be used to do validation on a form.
14248              * @param {Roo.bootstrap.TabPanel} this
14249              * @return {Boolean} false if there is an error
14250             
14251          */
14252         'beforedeactivate': true
14253      });
14254     
14255     this.tabId = this.tabId || Roo.id();
14256   
14257 };
14258
14259 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14260     
14261     active: false,
14262     html: false,
14263     tabId: false,
14264     navId : false,
14265     
14266     getAutoCreate : function(){
14267         var cfg = {
14268             tag: 'div',
14269             // item is needed for carousel - not sure if it has any effect otherwise
14270             cls: 'tab-pane item',
14271             html: this.html || ''
14272         };
14273         
14274         if(this.active){
14275             cfg.cls += ' active';
14276         }
14277         
14278         if(this.tabId){
14279             cfg.tabId = this.tabId;
14280         }
14281         
14282         
14283         return cfg;
14284     },
14285     
14286     initEvents:  function()
14287     {
14288         Roo.log('-------- init events on tab panel ---------');
14289         
14290         var p = this.parent();
14291         this.navId = this.navId || p.navId;
14292         
14293         if (typeof(this.navId) != 'undefined') {
14294             // not really needed.. but just in case.. parent should be a NavGroup.
14295             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14296             Roo.log(['register', tg, this]);
14297             tg.register(this);
14298         }
14299     },
14300     
14301     
14302     onRender : function(ct, position)
14303     {
14304        // Roo.log("Call onRender: " + this.xtype);
14305         
14306         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14307         
14308         
14309         
14310         
14311         
14312     },
14313     
14314     setActive: function(state)
14315     {
14316         Roo.log("panel - set active " + this.tabId + "=" + state);
14317         
14318         this.active = state;
14319         if (!state) {
14320             this.el.removeClass('active');
14321             
14322         } else  if (!this.el.hasClass('active')) {
14323             this.el.addClass('active');
14324         }
14325         this.fireEvent('changed', this, state);
14326     }
14327     
14328     
14329 });
14330  
14331
14332  
14333
14334  /*
14335  * - LGPL
14336  *
14337  * DateField
14338  * 
14339  */
14340
14341 /**
14342  * @class Roo.bootstrap.DateField
14343  * @extends Roo.bootstrap.Input
14344  * Bootstrap DateField class
14345  * @cfg {Number} weekStart default 0
14346  * @cfg {Number} weekStart default 0
14347  * @cfg {Number} viewMode default empty, (months|years)
14348  * @cfg {Number} minViewMode default empty, (months|years)
14349  * @cfg {Number} startDate default -Infinity
14350  * @cfg {Number} endDate default Infinity
14351  * @cfg {Boolean} todayHighlight default false
14352  * @cfg {Boolean} todayBtn default false
14353  * @cfg {Boolean} calendarWeeks default false
14354  * @cfg {Object} daysOfWeekDisabled default empty
14355  * 
14356  * @cfg {Boolean} keyboardNavigation default true
14357  * @cfg {String} language default en
14358  * 
14359  * @constructor
14360  * Create a new DateField
14361  * @param {Object} config The config object
14362  */
14363
14364 Roo.bootstrap.DateField = function(config){
14365     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14366      this.addEvents({
14367             /**
14368              * @event show
14369              * Fires when this field show.
14370              * @param {Roo.bootstrap.DateField} this
14371              * @param {Mixed} date The date value
14372              */
14373             show : true,
14374             /**
14375              * @event show
14376              * Fires when this field hide.
14377              * @param {Roo.bootstrap.DateField} this
14378              * @param {Mixed} date The date value
14379              */
14380             hide : true,
14381             /**
14382              * @event select
14383              * Fires when select a date.
14384              * @param {Roo.bootstrap.DateField} this
14385              * @param {Mixed} date The date value
14386              */
14387             select : true
14388         });
14389 };
14390
14391 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14392     
14393     /**
14394      * @cfg {String} format
14395      * The default date format string which can be overriden for localization support.  The format must be
14396      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14397      */
14398     format : "m/d/y",
14399     /**
14400      * @cfg {String} altFormats
14401      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14402      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14403      */
14404     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14405     
14406     weekStart : 0,
14407     
14408     viewMode : '',
14409     
14410     minViewMode : '',
14411     
14412     todayHighlight : false,
14413     
14414     todayBtn: false,
14415     
14416     language: 'en',
14417     
14418     keyboardNavigation: true,
14419     
14420     calendarWeeks: false,
14421     
14422     startDate: -Infinity,
14423     
14424     endDate: Infinity,
14425     
14426     daysOfWeekDisabled: [],
14427     
14428     _events: [],
14429     
14430     UTCDate: function()
14431     {
14432         return new Date(Date.UTC.apply(Date, arguments));
14433     },
14434     
14435     UTCToday: function()
14436     {
14437         var today = new Date();
14438         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14439     },
14440     
14441     getDate: function() {
14442             var d = this.getUTCDate();
14443             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14444     },
14445     
14446     getUTCDate: function() {
14447             return this.date;
14448     },
14449     
14450     setDate: function(d) {
14451             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14452     },
14453     
14454     setUTCDate: function(d) {
14455             this.date = d;
14456             this.setValue(this.formatDate(this.date));
14457     },
14458         
14459     onRender: function(ct, position)
14460     {
14461         
14462         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14463         
14464         this.language = this.language || 'en';
14465         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14466         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14467         
14468         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14469         this.format = this.format || 'm/d/y';
14470         this.isInline = false;
14471         this.isInput = true;
14472         this.component = this.el.select('.add-on', true).first() || false;
14473         this.component = (this.component && this.component.length === 0) ? false : this.component;
14474         this.hasInput = this.component && this.inputEL().length;
14475         
14476         if (typeof(this.minViewMode === 'string')) {
14477             switch (this.minViewMode) {
14478                 case 'months':
14479                     this.minViewMode = 1;
14480                     break;
14481                 case 'years':
14482                     this.minViewMode = 2;
14483                     break;
14484                 default:
14485                     this.minViewMode = 0;
14486                     break;
14487             }
14488         }
14489         
14490         if (typeof(this.viewMode === 'string')) {
14491             switch (this.viewMode) {
14492                 case 'months':
14493                     this.viewMode = 1;
14494                     break;
14495                 case 'years':
14496                     this.viewMode = 2;
14497                     break;
14498                 default:
14499                     this.viewMode = 0;
14500                     break;
14501             }
14502         }
14503                 
14504         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14505         
14506 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14507         
14508         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14509         
14510         this.picker().on('mousedown', this.onMousedown, this);
14511         this.picker().on('click', this.onClick, this);
14512         
14513         this.picker().addClass('datepicker-dropdown');
14514         
14515         this.startViewMode = this.viewMode;
14516         
14517         
14518         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14519             if(!this.calendarWeeks){
14520                 v.remove();
14521                 return;
14522             };
14523             
14524             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14525             v.attr('colspan', function(i, val){
14526                 return parseInt(val) + 1;
14527             });
14528         })
14529                         
14530         
14531         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14532         
14533         this.setStartDate(this.startDate);
14534         this.setEndDate(this.endDate);
14535         
14536         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14537         
14538         this.fillDow();
14539         this.fillMonths();
14540         this.update();
14541         this.showMode();
14542         
14543         if(this.isInline) {
14544             this.show();
14545         }
14546     },
14547     
14548     picker : function()
14549     {
14550         return this.pickerEl;
14551 //        return this.el.select('.datepicker', true).first();
14552     },
14553     
14554     fillDow: function()
14555     {
14556         var dowCnt = this.weekStart;
14557         
14558         var dow = {
14559             tag: 'tr',
14560             cn: [
14561                 
14562             ]
14563         };
14564         
14565         if(this.calendarWeeks){
14566             dow.cn.push({
14567                 tag: 'th',
14568                 cls: 'cw',
14569                 html: '&nbsp;'
14570             })
14571         }
14572         
14573         while (dowCnt < this.weekStart + 7) {
14574             dow.cn.push({
14575                 tag: 'th',
14576                 cls: 'dow',
14577                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14578             });
14579         }
14580         
14581         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14582     },
14583     
14584     fillMonths: function()
14585     {    
14586         var i = 0
14587         var months = this.picker().select('>.datepicker-months td', true).first();
14588         
14589         months.dom.innerHTML = '';
14590         
14591         while (i < 12) {
14592             var month = {
14593                 tag: 'span',
14594                 cls: 'month',
14595                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14596             }
14597             
14598             months.createChild(month);
14599         }
14600         
14601     },
14602     
14603     update: function()
14604     {
14605         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;
14606         
14607         if (this.date < this.startDate) {
14608             this.viewDate = new Date(this.startDate);
14609         } else if (this.date > this.endDate) {
14610             this.viewDate = new Date(this.endDate);
14611         } else {
14612             this.viewDate = new Date(this.date);
14613         }
14614         
14615         this.fill();
14616     },
14617     
14618     fill: function() 
14619     {
14620         var d = new Date(this.viewDate),
14621                 year = d.getUTCFullYear(),
14622                 month = d.getUTCMonth(),
14623                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14624                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14625                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14626                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14627                 currentDate = this.date && this.date.valueOf(),
14628                 today = this.UTCToday();
14629         
14630         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14631         
14632 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14633         
14634 //        this.picker.select('>tfoot th.today').
14635 //                                              .text(dates[this.language].today)
14636 //                                              .toggle(this.todayBtn !== false);
14637     
14638         this.updateNavArrows();
14639         this.fillMonths();
14640                                                 
14641         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14642         
14643         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14644          
14645         prevMonth.setUTCDate(day);
14646         
14647         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14648         
14649         var nextMonth = new Date(prevMonth);
14650         
14651         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14652         
14653         nextMonth = nextMonth.valueOf();
14654         
14655         var fillMonths = false;
14656         
14657         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14658         
14659         while(prevMonth.valueOf() < nextMonth) {
14660             var clsName = '';
14661             
14662             if (prevMonth.getUTCDay() === this.weekStart) {
14663                 if(fillMonths){
14664                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14665                 }
14666                     
14667                 fillMonths = {
14668                     tag: 'tr',
14669                     cn: []
14670                 };
14671                 
14672                 if(this.calendarWeeks){
14673                     // ISO 8601: First week contains first thursday.
14674                     // ISO also states week starts on Monday, but we can be more abstract here.
14675                     var
14676                     // Start of current week: based on weekstart/current date
14677                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14678                     // Thursday of this week
14679                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14680                     // First Thursday of year, year from thursday
14681                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14682                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14683                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14684                     
14685                     fillMonths.cn.push({
14686                         tag: 'td',
14687                         cls: 'cw',
14688                         html: calWeek
14689                     });
14690                 }
14691             }
14692             
14693             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14694                 clsName += ' old';
14695             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14696                 clsName += ' new';
14697             }
14698             if (this.todayHighlight &&
14699                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14700                 prevMonth.getUTCMonth() == today.getMonth() &&
14701                 prevMonth.getUTCDate() == today.getDate()) {
14702                 clsName += ' today';
14703             }
14704             
14705             if (currentDate && prevMonth.valueOf() === currentDate) {
14706                 clsName += ' active';
14707             }
14708             
14709             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14710                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14711                     clsName += ' disabled';
14712             }
14713             
14714             fillMonths.cn.push({
14715                 tag: 'td',
14716                 cls: 'day ' + clsName,
14717                 html: prevMonth.getDate()
14718             })
14719             
14720             prevMonth.setDate(prevMonth.getDate()+1);
14721         }
14722           
14723         var currentYear = this.date && this.date.getUTCFullYear();
14724         var currentMonth = this.date && this.date.getUTCMonth();
14725         
14726         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14727         
14728         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14729             v.removeClass('active');
14730             
14731             if(currentYear === year && k === currentMonth){
14732                 v.addClass('active');
14733             }
14734             
14735             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14736                 v.addClass('disabled');
14737             }
14738             
14739         });
14740         
14741         
14742         year = parseInt(year/10, 10) * 10;
14743         
14744         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14745         
14746         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14747         
14748         year -= 1;
14749         for (var i = -1; i < 11; i++) {
14750             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14751                 tag: 'span',
14752                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14753                 html: year
14754             })
14755             
14756             year += 1;
14757         }
14758     },
14759     
14760     showMode: function(dir) 
14761     {
14762         if (dir) {
14763             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14764         }
14765         Roo.each(this.picker().select('>div',true).elements, function(v){
14766             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14767             v.hide();
14768         });
14769         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14770     },
14771     
14772     place: function()
14773     {
14774         if(this.isInline) return;
14775         
14776         this.picker().removeClass(['bottom', 'top']);
14777         
14778         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14779             /*
14780              * place to the top of element!
14781              *
14782              */
14783             
14784             this.picker().addClass('top');
14785             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14786             
14787             return;
14788         }
14789         
14790         this.picker().addClass('bottom');
14791         
14792         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14793     },
14794     
14795     parseDate : function(value)
14796     {
14797         if(!value || value instanceof Date){
14798             return value;
14799         }
14800         var v = Date.parseDate(value, this.format);
14801         if (!v && this.useIso) {
14802             v = Date.parseDate(value, 'Y-m-d');
14803         }
14804         if(!v && this.altFormats){
14805             if(!this.altFormatsArray){
14806                 this.altFormatsArray = this.altFormats.split("|");
14807             }
14808             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14809                 v = Date.parseDate(value, this.altFormatsArray[i]);
14810             }
14811         }
14812         return v;
14813     },
14814     
14815     formatDate : function(date, fmt)
14816     {
14817         return (!date || !(date instanceof Date)) ?
14818         date : date.dateFormat(fmt || this.format);
14819     },
14820     
14821     onFocus : function()
14822     {
14823         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14824         this.show();
14825     },
14826     
14827     onBlur : function()
14828     {
14829         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14830         
14831         var d = this.inputEl().getValue();
14832         
14833         this.setValue(d);
14834                 
14835         this.hide();
14836     },
14837     
14838     show : function()
14839     {
14840         this.picker().show();
14841         this.update();
14842         this.place();
14843         
14844         this.fireEvent('show', this, this.date);
14845     },
14846     
14847     hide : function()
14848     {
14849         if(this.isInline) return;
14850         this.picker().hide();
14851         this.viewMode = this.startViewMode;
14852         this.showMode();
14853         
14854         this.fireEvent('hide', this, this.date);
14855         
14856     },
14857     
14858     onMousedown: function(e)
14859     {
14860         e.stopPropagation();
14861         e.preventDefault();
14862     },
14863     
14864     keyup: function(e)
14865     {
14866         Roo.bootstrap.DateField.superclass.keyup.call(this);
14867         this.update();
14868     },
14869
14870     setValue: function(v)
14871     {
14872         var d = new Date(v).clearTime();
14873         
14874         if(isNaN(d.getTime())){
14875             this.date = this.viewDate = '';
14876             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14877             return;
14878         }
14879         
14880         v = this.formatDate(d);
14881         
14882         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14883         
14884         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14885      
14886         this.update();
14887
14888         this.fireEvent('select', this, this.date);
14889         
14890     },
14891     
14892     getValue: function()
14893     {
14894         return this.formatDate(this.date);
14895     },
14896     
14897     fireKey: function(e)
14898     {
14899         if (!this.picker().isVisible()){
14900             if (e.keyCode == 27) // allow escape to hide and re-show picker
14901                 this.show();
14902             return;
14903         }
14904         
14905         var dateChanged = false,
14906         dir, day, month,
14907         newDate, newViewDate;
14908         
14909         switch(e.keyCode){
14910             case 27: // escape
14911                 this.hide();
14912                 e.preventDefault();
14913                 break;
14914             case 37: // left
14915             case 39: // right
14916                 if (!this.keyboardNavigation) break;
14917                 dir = e.keyCode == 37 ? -1 : 1;
14918                 
14919                 if (e.ctrlKey){
14920                     newDate = this.moveYear(this.date, dir);
14921                     newViewDate = this.moveYear(this.viewDate, dir);
14922                 } else if (e.shiftKey){
14923                     newDate = this.moveMonth(this.date, dir);
14924                     newViewDate = this.moveMonth(this.viewDate, dir);
14925                 } else {
14926                     newDate = new Date(this.date);
14927                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14928                     newViewDate = new Date(this.viewDate);
14929                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14930                 }
14931                 if (this.dateWithinRange(newDate)){
14932                     this.date = newDate;
14933                     this.viewDate = newViewDate;
14934                     this.setValue(this.formatDate(this.date));
14935 //                    this.update();
14936                     e.preventDefault();
14937                     dateChanged = true;
14938                 }
14939                 break;
14940             case 38: // up
14941             case 40: // down
14942                 if (!this.keyboardNavigation) break;
14943                 dir = e.keyCode == 38 ? -1 : 1;
14944                 if (e.ctrlKey){
14945                     newDate = this.moveYear(this.date, dir);
14946                     newViewDate = this.moveYear(this.viewDate, dir);
14947                 } else if (e.shiftKey){
14948                     newDate = this.moveMonth(this.date, dir);
14949                     newViewDate = this.moveMonth(this.viewDate, dir);
14950                 } else {
14951                     newDate = new Date(this.date);
14952                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14953                     newViewDate = new Date(this.viewDate);
14954                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14955                 }
14956                 if (this.dateWithinRange(newDate)){
14957                     this.date = newDate;
14958                     this.viewDate = newViewDate;
14959                     this.setValue(this.formatDate(this.date));
14960 //                    this.update();
14961                     e.preventDefault();
14962                     dateChanged = true;
14963                 }
14964                 break;
14965             case 13: // enter
14966                 this.setValue(this.formatDate(this.date));
14967                 this.hide();
14968                 e.preventDefault();
14969                 break;
14970             case 9: // tab
14971                 this.setValue(this.formatDate(this.date));
14972                 this.hide();
14973                 break;
14974             case 16: // shift
14975             case 17: // ctrl
14976             case 18: // alt
14977                 break;
14978             default :
14979                 this.hide();
14980                 
14981         }
14982     },
14983     
14984     
14985     onClick: function(e) 
14986     {
14987         e.stopPropagation();
14988         e.preventDefault();
14989         
14990         var target = e.getTarget();
14991         
14992         if(target.nodeName.toLowerCase() === 'i'){
14993             target = Roo.get(target).dom.parentNode;
14994         }
14995         
14996         var nodeName = target.nodeName;
14997         var className = target.className;
14998         var html = target.innerHTML;
14999         
15000         switch(nodeName.toLowerCase()) {
15001             case 'th':
15002                 switch(className) {
15003                     case 'switch':
15004                         this.showMode(1);
15005                         break;
15006                     case 'prev':
15007                     case 'next':
15008                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15009                         switch(this.viewMode){
15010                                 case 0:
15011                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15012                                         break;
15013                                 case 1:
15014                                 case 2:
15015                                         this.viewDate = this.moveYear(this.viewDate, dir);
15016                                         break;
15017                         }
15018                         this.fill();
15019                         break;
15020                     case 'today':
15021                         var date = new Date();
15022                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15023 //                        this.fill()
15024                         this.setValue(this.formatDate(this.date));
15025                         
15026                         this.hide();
15027                         break;
15028                 }
15029                 break;
15030             case 'span':
15031                 if (className.indexOf('disabled') === -1) {
15032                     this.viewDate.setUTCDate(1);
15033                     if (className.indexOf('month') !== -1) {
15034                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15035                     } else {
15036                         var year = parseInt(html, 10) || 0;
15037                         this.viewDate.setUTCFullYear(year);
15038                         
15039                     }
15040                     this.showMode(-1);
15041                     this.fill();
15042                 }
15043                 break;
15044                 
15045             case 'td':
15046                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15047                     var day = parseInt(html, 10) || 1;
15048                     var year = this.viewDate.getUTCFullYear(),
15049                         month = this.viewDate.getUTCMonth();
15050
15051                     if (className.indexOf('old') !== -1) {
15052                         if(month === 0 ){
15053                             month = 11;
15054                             year -= 1;
15055                         }else{
15056                             month -= 1;
15057                         }
15058                     } else if (className.indexOf('new') !== -1) {
15059                         if (month == 11) {
15060                             month = 0;
15061                             year += 1;
15062                         } else {
15063                             month += 1;
15064                         }
15065                     }
15066                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15067                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15068 //                    this.fill();
15069                     this.setValue(this.formatDate(this.date));
15070                     this.hide();
15071                 }
15072                 break;
15073         }
15074     },
15075     
15076     setStartDate: function(startDate)
15077     {
15078         this.startDate = startDate || -Infinity;
15079         if (this.startDate !== -Infinity) {
15080             this.startDate = this.parseDate(this.startDate);
15081         }
15082         this.update();
15083         this.updateNavArrows();
15084     },
15085
15086     setEndDate: function(endDate)
15087     {
15088         this.endDate = endDate || Infinity;
15089         if (this.endDate !== Infinity) {
15090             this.endDate = this.parseDate(this.endDate);
15091         }
15092         this.update();
15093         this.updateNavArrows();
15094     },
15095     
15096     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15097     {
15098         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15099         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15100             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15101         }
15102         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15103             return parseInt(d, 10);
15104         });
15105         this.update();
15106         this.updateNavArrows();
15107     },
15108     
15109     updateNavArrows: function() 
15110     {
15111         var d = new Date(this.viewDate),
15112         year = d.getUTCFullYear(),
15113         month = d.getUTCMonth();
15114         
15115         Roo.each(this.picker().select('.prev', true).elements, function(v){
15116             v.show();
15117             switch (this.viewMode) {
15118                 case 0:
15119
15120                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15121                         v.hide();
15122                     }
15123                     break;
15124                 case 1:
15125                 case 2:
15126                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15127                         v.hide();
15128                     }
15129                     break;
15130             }
15131         });
15132         
15133         Roo.each(this.picker().select('.next', true).elements, function(v){
15134             v.show();
15135             switch (this.viewMode) {
15136                 case 0:
15137
15138                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15139                         v.hide();
15140                     }
15141                     break;
15142                 case 1:
15143                 case 2:
15144                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15145                         v.hide();
15146                     }
15147                     break;
15148             }
15149         })
15150     },
15151     
15152     moveMonth: function(date, dir)
15153     {
15154         if (!dir) return date;
15155         var new_date = new Date(date.valueOf()),
15156         day = new_date.getUTCDate(),
15157         month = new_date.getUTCMonth(),
15158         mag = Math.abs(dir),
15159         new_month, test;
15160         dir = dir > 0 ? 1 : -1;
15161         if (mag == 1){
15162             test = dir == -1
15163             // If going back one month, make sure month is not current month
15164             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15165             ? function(){
15166                 return new_date.getUTCMonth() == month;
15167             }
15168             // If going forward one month, make sure month is as expected
15169             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15170             : function(){
15171                 return new_date.getUTCMonth() != new_month;
15172             };
15173             new_month = month + dir;
15174             new_date.setUTCMonth(new_month);
15175             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15176             if (new_month < 0 || new_month > 11)
15177                 new_month = (new_month + 12) % 12;
15178         } else {
15179             // For magnitudes >1, move one month at a time...
15180             for (var i=0; i<mag; i++)
15181                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15182                 new_date = this.moveMonth(new_date, dir);
15183             // ...then reset the day, keeping it in the new month
15184             new_month = new_date.getUTCMonth();
15185             new_date.setUTCDate(day);
15186             test = function(){
15187                 return new_month != new_date.getUTCMonth();
15188             };
15189         }
15190         // Common date-resetting loop -- if date is beyond end of month, make it
15191         // end of month
15192         while (test()){
15193             new_date.setUTCDate(--day);
15194             new_date.setUTCMonth(new_month);
15195         }
15196         return new_date;
15197     },
15198
15199     moveYear: function(date, dir)
15200     {
15201         return this.moveMonth(date, dir*12);
15202     },
15203
15204     dateWithinRange: function(date)
15205     {
15206         return date >= this.startDate && date <= this.endDate;
15207     },
15208
15209     
15210     remove: function() 
15211     {
15212         this.picker().remove();
15213     }
15214    
15215 });
15216
15217 Roo.apply(Roo.bootstrap.DateField,  {
15218     
15219     head : {
15220         tag: 'thead',
15221         cn: [
15222         {
15223             tag: 'tr',
15224             cn: [
15225             {
15226                 tag: 'th',
15227                 cls: 'prev',
15228                 html: '<i class="fa fa-arrow-left"/>'
15229             },
15230             {
15231                 tag: 'th',
15232                 cls: 'switch',
15233                 colspan: '5'
15234             },
15235             {
15236                 tag: 'th',
15237                 cls: 'next',
15238                 html: '<i class="fa fa-arrow-right"/>'
15239             }
15240
15241             ]
15242         }
15243         ]
15244     },
15245     
15246     content : {
15247         tag: 'tbody',
15248         cn: [
15249         {
15250             tag: 'tr',
15251             cn: [
15252             {
15253                 tag: 'td',
15254                 colspan: '7'
15255             }
15256             ]
15257         }
15258         ]
15259     },
15260     
15261     footer : {
15262         tag: 'tfoot',
15263         cn: [
15264         {
15265             tag: 'tr',
15266             cn: [
15267             {
15268                 tag: 'th',
15269                 colspan: '7',
15270                 cls: 'today'
15271             }
15272                     
15273             ]
15274         }
15275         ]
15276     },
15277     
15278     dates:{
15279         en: {
15280             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15281             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15282             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15283             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15284             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15285             today: "Today"
15286         }
15287     },
15288     
15289     modes: [
15290     {
15291         clsName: 'days',
15292         navFnc: 'Month',
15293         navStep: 1
15294     },
15295     {
15296         clsName: 'months',
15297         navFnc: 'FullYear',
15298         navStep: 1
15299     },
15300     {
15301         clsName: 'years',
15302         navFnc: 'FullYear',
15303         navStep: 10
15304     }]
15305 });
15306
15307 Roo.apply(Roo.bootstrap.DateField,  {
15308   
15309     template : {
15310         tag: 'div',
15311         cls: 'datepicker dropdown-menu',
15312         cn: [
15313         {
15314             tag: 'div',
15315             cls: 'datepicker-days',
15316             cn: [
15317             {
15318                 tag: 'table',
15319                 cls: 'table-condensed',
15320                 cn:[
15321                 Roo.bootstrap.DateField.head,
15322                 {
15323                     tag: 'tbody'
15324                 },
15325                 Roo.bootstrap.DateField.footer
15326                 ]
15327             }
15328             ]
15329         },
15330         {
15331             tag: 'div',
15332             cls: 'datepicker-months',
15333             cn: [
15334             {
15335                 tag: 'table',
15336                 cls: 'table-condensed',
15337                 cn:[
15338                 Roo.bootstrap.DateField.head,
15339                 Roo.bootstrap.DateField.content,
15340                 Roo.bootstrap.DateField.footer
15341                 ]
15342             }
15343             ]
15344         },
15345         {
15346             tag: 'div',
15347             cls: 'datepicker-years',
15348             cn: [
15349             {
15350                 tag: 'table',
15351                 cls: 'table-condensed',
15352                 cn:[
15353                 Roo.bootstrap.DateField.head,
15354                 Roo.bootstrap.DateField.content,
15355                 Roo.bootstrap.DateField.footer
15356                 ]
15357             }
15358             ]
15359         }
15360         ]
15361     }
15362 });
15363
15364  
15365
15366  /*
15367  * - LGPL
15368  *
15369  * TimeField
15370  * 
15371  */
15372
15373 /**
15374  * @class Roo.bootstrap.TimeField
15375  * @extends Roo.bootstrap.Input
15376  * Bootstrap DateField class
15377  * 
15378  * 
15379  * @constructor
15380  * Create a new TimeField
15381  * @param {Object} config The config object
15382  */
15383
15384 Roo.bootstrap.TimeField = function(config){
15385     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15386     this.addEvents({
15387             /**
15388              * @event show
15389              * Fires when this field show.
15390              * @param {Roo.bootstrap.DateField} this
15391              * @param {Mixed} date The date value
15392              */
15393             show : true,
15394             /**
15395              * @event show
15396              * Fires when this field hide.
15397              * @param {Roo.bootstrap.DateField} this
15398              * @param {Mixed} date The date value
15399              */
15400             hide : true,
15401             /**
15402              * @event select
15403              * Fires when select a date.
15404              * @param {Roo.bootstrap.DateField} this
15405              * @param {Mixed} date The date value
15406              */
15407             select : true
15408         });
15409 };
15410
15411 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15412     
15413     /**
15414      * @cfg {String} format
15415      * The default time format string which can be overriden for localization support.  The format must be
15416      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15417      */
15418     format : "H:i",
15419        
15420     onRender: function(ct, position)
15421     {
15422         
15423         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15424                 
15425         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15426         
15427         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15428         
15429         this.pop = this.picker().select('>.datepicker-time',true).first();
15430         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15431         
15432         this.picker().on('mousedown', this.onMousedown, this);
15433         this.picker().on('click', this.onClick, this);
15434         
15435         this.picker().addClass('datepicker-dropdown');
15436     
15437         this.fillTime();
15438         this.update();
15439             
15440         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15441         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15442         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15443         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15444         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15445         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15446
15447     },
15448     
15449     fireKey: function(e){
15450         if (!this.picker().isVisible()){
15451             if (e.keyCode == 27) // allow escape to hide and re-show picker
15452                 this.show();
15453             return;
15454         }
15455
15456         e.preventDefault();
15457         
15458         switch(e.keyCode){
15459             case 27: // escape
15460                 this.hide();
15461                 break;
15462             case 37: // left
15463             case 39: // right
15464                 this.onTogglePeriod();
15465                 break;
15466             case 38: // up
15467                 this.onIncrementMinutes();
15468                 break;
15469             case 40: // down
15470                 this.onDecrementMinutes();
15471                 break;
15472             case 13: // enter
15473             case 9: // tab
15474                 this.setTime();
15475                 break;
15476         }
15477     },
15478     
15479     onClick: function(e) {
15480         e.stopPropagation();
15481         e.preventDefault();
15482     },
15483     
15484     picker : function()
15485     {
15486         return this.el.select('.datepicker', true).first();
15487     },
15488     
15489     fillTime: function()
15490     {    
15491         var time = this.pop.select('tbody', true).first();
15492         
15493         time.dom.innerHTML = '';
15494         
15495         time.createChild({
15496             tag: 'tr',
15497             cn: [
15498                 {
15499                     tag: 'td',
15500                     cn: [
15501                         {
15502                             tag: 'a',
15503                             href: '#',
15504                             cls: 'btn',
15505                             cn: [
15506                                 {
15507                                     tag: 'span',
15508                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15509                                 }
15510                             ]
15511                         } 
15512                     ]
15513                 },
15514                 {
15515                     tag: 'td',
15516                     cls: 'separator'
15517                 },
15518                 {
15519                     tag: 'td',
15520                     cn: [
15521                         {
15522                             tag: 'a',
15523                             href: '#',
15524                             cls: 'btn',
15525                             cn: [
15526                                 {
15527                                     tag: 'span',
15528                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15529                                 }
15530                             ]
15531                         }
15532                     ]
15533                 },
15534                 {
15535                     tag: 'td',
15536                     cls: 'separator'
15537                 }
15538             ]
15539         });
15540         
15541         time.createChild({
15542             tag: 'tr',
15543             cn: [
15544                 {
15545                     tag: 'td',
15546                     cn: [
15547                         {
15548                             tag: 'span',
15549                             cls: 'timepicker-hour',
15550                             html: '00'
15551                         }  
15552                     ]
15553                 },
15554                 {
15555                     tag: 'td',
15556                     cls: 'separator',
15557                     html: ':'
15558                 },
15559                 {
15560                     tag: 'td',
15561                     cn: [
15562                         {
15563                             tag: 'span',
15564                             cls: 'timepicker-minute',
15565                             html: '00'
15566                         }  
15567                     ]
15568                 },
15569                 {
15570                     tag: 'td',
15571                     cls: 'separator'
15572                 },
15573                 {
15574                     tag: 'td',
15575                     cn: [
15576                         {
15577                             tag: 'button',
15578                             type: 'button',
15579                             cls: 'btn btn-primary period',
15580                             html: 'AM'
15581                             
15582                         }
15583                     ]
15584                 }
15585             ]
15586         });
15587         
15588         time.createChild({
15589             tag: 'tr',
15590             cn: [
15591                 {
15592                     tag: 'td',
15593                     cn: [
15594                         {
15595                             tag: 'a',
15596                             href: '#',
15597                             cls: 'btn',
15598                             cn: [
15599                                 {
15600                                     tag: 'span',
15601                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15602                                 }
15603                             ]
15604                         }
15605                     ]
15606                 },
15607                 {
15608                     tag: 'td',
15609                     cls: 'separator'
15610                 },
15611                 {
15612                     tag: 'td',
15613                     cn: [
15614                         {
15615                             tag: 'a',
15616                             href: '#',
15617                             cls: 'btn',
15618                             cn: [
15619                                 {
15620                                     tag: 'span',
15621                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15622                                 }
15623                             ]
15624                         }
15625                     ]
15626                 },
15627                 {
15628                     tag: 'td',
15629                     cls: 'separator'
15630                 }
15631             ]
15632         });
15633         
15634     },
15635     
15636     update: function()
15637     {
15638         
15639         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15640         
15641         this.fill();
15642     },
15643     
15644     fill: function() 
15645     {
15646         var hours = this.time.getHours();
15647         var minutes = this.time.getMinutes();
15648         var period = 'AM';
15649         
15650         if(hours > 11){
15651             period = 'PM';
15652         }
15653         
15654         if(hours == 0){
15655             hours = 12;
15656         }
15657         
15658         
15659         if(hours > 12){
15660             hours = hours - 12;
15661         }
15662         
15663         if(hours < 10){
15664             hours = '0' + hours;
15665         }
15666         
15667         if(minutes < 10){
15668             minutes = '0' + minutes;
15669         }
15670         
15671         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15672         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15673         this.pop.select('button', true).first().dom.innerHTML = period;
15674         
15675     },
15676     
15677     place: function()
15678     {   
15679         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15680         
15681         var cls = ['bottom'];
15682         
15683         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15684             cls.pop();
15685             cls.push('top');
15686         }
15687         
15688         cls.push('right');
15689         
15690         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15691             cls.pop();
15692             cls.push('left');
15693         }
15694         
15695         this.picker().addClass(cls.join('-'));
15696         
15697         var _this = this;
15698         
15699         Roo.each(cls, function(c){
15700             if(c == 'bottom'){
15701                 _this.picker().setTop(_this.inputEl().getHeight());
15702                 return;
15703             }
15704             if(c == 'top'){
15705                 _this.picker().setTop(0 - _this.picker().getHeight());
15706                 return;
15707             }
15708             
15709             if(c == 'left'){
15710                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15711                 return;
15712             }
15713             if(c == 'right'){
15714                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15715                 return;
15716             }
15717         });
15718         
15719     },
15720   
15721     onFocus : function()
15722     {
15723         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15724         this.show();
15725     },
15726     
15727     onBlur : function()
15728     {
15729         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15730         this.hide();
15731     },
15732     
15733     show : function()
15734     {
15735         this.picker().show();
15736         this.pop.show();
15737         this.update();
15738         this.place();
15739         
15740         this.fireEvent('show', this, this.date);
15741     },
15742     
15743     hide : function()
15744     {
15745         this.picker().hide();
15746         this.pop.hide();
15747         
15748         this.fireEvent('hide', this, this.date);
15749     },
15750     
15751     setTime : function()
15752     {
15753         this.hide();
15754         this.setValue(this.time.format(this.format));
15755         
15756         this.fireEvent('select', this, this.date);
15757         
15758         
15759     },
15760     
15761     onMousedown: function(e){
15762         e.stopPropagation();
15763         e.preventDefault();
15764     },
15765     
15766     onIncrementHours: function()
15767     {
15768         Roo.log('onIncrementHours');
15769         this.time = this.time.add(Date.HOUR, 1);
15770         this.update();
15771         
15772     },
15773     
15774     onDecrementHours: function()
15775     {
15776         Roo.log('onDecrementHours');
15777         this.time = this.time.add(Date.HOUR, -1);
15778         this.update();
15779     },
15780     
15781     onIncrementMinutes: function()
15782     {
15783         Roo.log('onIncrementMinutes');
15784         this.time = this.time.add(Date.MINUTE, 1);
15785         this.update();
15786     },
15787     
15788     onDecrementMinutes: function()
15789     {
15790         Roo.log('onDecrementMinutes');
15791         this.time = this.time.add(Date.MINUTE, -1);
15792         this.update();
15793     },
15794     
15795     onTogglePeriod: function()
15796     {
15797         Roo.log('onTogglePeriod');
15798         this.time = this.time.add(Date.HOUR, 12);
15799         this.update();
15800     }
15801     
15802    
15803 });
15804
15805 Roo.apply(Roo.bootstrap.TimeField,  {
15806     
15807     content : {
15808         tag: 'tbody',
15809         cn: [
15810             {
15811                 tag: 'tr',
15812                 cn: [
15813                 {
15814                     tag: 'td',
15815                     colspan: '7'
15816                 }
15817                 ]
15818             }
15819         ]
15820     },
15821     
15822     footer : {
15823         tag: 'tfoot',
15824         cn: [
15825             {
15826                 tag: 'tr',
15827                 cn: [
15828                 {
15829                     tag: 'th',
15830                     colspan: '7',
15831                     cls: '',
15832                     cn: [
15833                         {
15834                             tag: 'button',
15835                             cls: 'btn btn-info ok',
15836                             html: 'OK'
15837                         }
15838                     ]
15839                 }
15840
15841                 ]
15842             }
15843         ]
15844     }
15845 });
15846
15847 Roo.apply(Roo.bootstrap.TimeField,  {
15848   
15849     template : {
15850         tag: 'div',
15851         cls: 'datepicker dropdown-menu',
15852         cn: [
15853             {
15854                 tag: 'div',
15855                 cls: 'datepicker-time',
15856                 cn: [
15857                 {
15858                     tag: 'table',
15859                     cls: 'table-condensed',
15860                     cn:[
15861                     Roo.bootstrap.TimeField.content,
15862                     Roo.bootstrap.TimeField.footer
15863                     ]
15864                 }
15865                 ]
15866             }
15867         ]
15868     }
15869 });
15870
15871  
15872
15873  /*
15874  * - LGPL
15875  *
15876  * CheckBox
15877  * 
15878  */
15879
15880 /**
15881  * @class Roo.bootstrap.CheckBox
15882  * @extends Roo.bootstrap.Input
15883  * Bootstrap CheckBox class
15884  * 
15885  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15886  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15887  * @cfg {String} boxLabel The text that appears beside the checkbox
15888  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15889  * @cfg {Boolean} checked initnal the element
15890  * 
15891  * 
15892  * @constructor
15893  * Create a new CheckBox
15894  * @param {Object} config The config object
15895  */
15896
15897 Roo.bootstrap.CheckBox = function(config){
15898     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15899    
15900         this.addEvents({
15901             /**
15902             * @event check
15903             * Fires when the element is checked or unchecked.
15904             * @param {Roo.bootstrap.CheckBox} this This input
15905             * @param {Boolean} checked The new checked value
15906             */
15907            check : true
15908         });
15909 };
15910
15911 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15912     
15913     inputType: 'checkbox',
15914     inputValue: 1,
15915     valueOff: 0,
15916     boxLabel: false,
15917     checked: false,
15918     weight : false,
15919     
15920     getAutoCreate : function()
15921     {
15922         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15923         
15924         var id = Roo.id();
15925         
15926         var cfg = {};
15927         
15928         cfg.cls = 'form-group checkbox' //input-group
15929         
15930         
15931         
15932         
15933         var input =  {
15934             tag: 'input',
15935             id : id,
15936             type : this.inputType,
15937             value : (!this.checked) ? this.valueOff : this.inputValue,
15938             cls : 'roo-checkbox', //'form-box',
15939             placeholder : this.placeholder || ''
15940             
15941         };
15942         
15943         if (this.weight) { // Validity check?
15944             cfg.cls += " checkbox-" + this.weight;
15945         }
15946         
15947         if (this.disabled) {
15948             input.disabled=true;
15949         }
15950         
15951         if(this.checked){
15952             input.checked = this.checked;
15953         }
15954         
15955         if (this.name) {
15956             input.name = this.name;
15957         }
15958         
15959         if (this.size) {
15960             input.cls += ' input-' + this.size;
15961         }
15962         
15963         var settings=this;
15964         ['xs','sm','md','lg'].map(function(size){
15965             if (settings[size]) {
15966                 cfg.cls += ' col-' + size + '-' + settings[size];
15967             }
15968         });
15969         
15970        
15971         
15972         var inputblock = input;
15973         
15974         
15975         
15976         
15977         if (this.before || this.after) {
15978             
15979             inputblock = {
15980                 cls : 'input-group',
15981                 cn :  [] 
15982             };
15983             if (this.before) {
15984                 inputblock.cn.push({
15985                     tag :'span',
15986                     cls : 'input-group-addon',
15987                     html : this.before
15988                 });
15989             }
15990             inputblock.cn.push(input);
15991             if (this.after) {
15992                 inputblock.cn.push({
15993                     tag :'span',
15994                     cls : 'input-group-addon',
15995                     html : this.after
15996                 });
15997             }
15998             
15999         };
16000         
16001         if (align ==='left' && this.fieldLabel.length) {
16002                 Roo.log("left and has label");
16003                 cfg.cn = [
16004                     
16005                     {
16006                         tag: 'label',
16007                         'for' :  id,
16008                         cls : 'control-label col-md-' + this.labelWidth,
16009                         html : this.fieldLabel
16010                         
16011                     },
16012                     {
16013                         cls : "col-md-" + (12 - this.labelWidth), 
16014                         cn: [
16015                             inputblock
16016                         ]
16017                     }
16018                     
16019                 ];
16020         } else if ( this.fieldLabel.length) {
16021                 Roo.log(" label");
16022                 cfg.cn = [
16023                    
16024                     {
16025                         tag: this.boxLabel ? 'span' : 'label',
16026                         'for': id,
16027                         cls: 'control-label box-input-label',
16028                         //cls : 'input-group-addon',
16029                         html : this.fieldLabel
16030                         
16031                     },
16032                     
16033                     inputblock
16034                     
16035                 ];
16036
16037         } else {
16038             
16039                 Roo.log(" no label && no align");
16040                 cfg.cn = [  inputblock ] ;
16041                 
16042                 
16043         };
16044          if(this.boxLabel){
16045             cfg.cn.push( {
16046                 tag: 'label',
16047                 'for': id,
16048                 cls: 'box-label',
16049                 html: this.boxLabel
16050                 
16051             });
16052         }
16053         
16054         
16055        
16056         return cfg;
16057         
16058     },
16059     
16060     /**
16061      * return the real input element.
16062      */
16063     inputEl: function ()
16064     {
16065         return this.el.select('input.roo-checkbox',true).first();
16066     },
16067     
16068     label: function()
16069     {
16070         return this.el.select('label.control-label',true).first();
16071     },
16072     
16073     initEvents : function()
16074     {
16075 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16076         
16077         this.inputEl().on('click', this.onClick,  this);
16078         
16079     },
16080     
16081     onClick : function()
16082     {   
16083         this.setChecked(!this.checked);
16084     },
16085     
16086     setChecked : function(state,suppressEvent)
16087     {
16088         this.checked = state;
16089         
16090         this.inputEl().dom.checked = state;
16091         
16092         if(suppressEvent !== true){
16093             this.fireEvent('check', this, state);
16094         }
16095         
16096         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16097         
16098     },
16099     
16100     setValue : function(v,suppressEvent)
16101     {
16102         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16103     }
16104     
16105 });
16106
16107  
16108 /*
16109  * - LGPL
16110  *
16111  * Radio
16112  * 
16113  */
16114
16115 /**
16116  * @class Roo.bootstrap.Radio
16117  * @extends Roo.bootstrap.CheckBox
16118  * Bootstrap Radio class
16119
16120  * @constructor
16121  * Create a new Radio
16122  * @param {Object} config The config object
16123  */
16124
16125 Roo.bootstrap.Radio = function(config){
16126     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16127    
16128 };
16129
16130 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16131     
16132     inputType: 'radio',
16133     inputValue: '',
16134     valueOff: '',
16135     
16136     getAutoCreate : function()
16137     {
16138         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16139         
16140         var id = Roo.id();
16141         
16142         var cfg = {};
16143         
16144         cfg.cls = 'form-group radio' //input-group
16145         
16146         var input =  {
16147             tag: 'input',
16148             id : id,
16149             type : this.inputType,
16150             value : (!this.checked) ? this.valueOff : this.inputValue,
16151             cls : 'roo-radio',
16152             placeholder : this.placeholder || ''
16153             
16154         };
16155           if (this.weight) { // Validity check?
16156             cfg.cls += " radio-" + this.weight;
16157         }
16158         if (this.disabled) {
16159             input.disabled=true;
16160         }
16161         
16162         if(this.checked){
16163             input.checked = this.checked;
16164         }
16165         
16166         if (this.name) {
16167             input.name = this.name;
16168         }
16169         
16170         if (this.size) {
16171             input.cls += ' input-' + this.size;
16172         }
16173         
16174         var settings=this;
16175         ['xs','sm','md','lg'].map(function(size){
16176             if (settings[size]) {
16177                 cfg.cls += ' col-' + size + '-' + settings[size];
16178             }
16179         });
16180         
16181         var inputblock = input;
16182         
16183         if (this.before || this.after) {
16184             
16185             inputblock = {
16186                 cls : 'input-group',
16187                 cn :  [] 
16188             };
16189             if (this.before) {
16190                 inputblock.cn.push({
16191                     tag :'span',
16192                     cls : 'input-group-addon',
16193                     html : this.before
16194                 });
16195             }
16196             inputblock.cn.push(input);
16197             if (this.after) {
16198                 inputblock.cn.push({
16199                     tag :'span',
16200                     cls : 'input-group-addon',
16201                     html : this.after
16202                 });
16203             }
16204             
16205         };
16206         
16207         if (align ==='left' && this.fieldLabel.length) {
16208                 Roo.log("left and has label");
16209                 cfg.cn = [
16210                     
16211                     {
16212                         tag: 'label',
16213                         'for' :  id,
16214                         cls : 'control-label col-md-' + this.labelWidth,
16215                         html : this.fieldLabel
16216                         
16217                     },
16218                     {
16219                         cls : "col-md-" + (12 - this.labelWidth), 
16220                         cn: [
16221                             inputblock
16222                         ]
16223                     }
16224                     
16225                 ];
16226         } else if ( this.fieldLabel.length) {
16227                 Roo.log(" label");
16228                  cfg.cn = [
16229                    
16230                     {
16231                         tag: 'label',
16232                         'for': id,
16233                         cls: 'control-label box-input-label',
16234                         //cls : 'input-group-addon',
16235                         html : this.fieldLabel
16236                         
16237                     },
16238                     
16239                     inputblock
16240                     
16241                 ];
16242
16243         } else {
16244             
16245                    Roo.log(" no label && no align");
16246                 cfg.cn = [
16247                     
16248                         inputblock
16249                     
16250                 ];
16251                 
16252                 
16253         };
16254         
16255         if(this.boxLabel){
16256             cfg.cn.push({
16257                 tag: 'label',
16258                 'for': id,
16259                 cls: 'box-label',
16260                 html: this.boxLabel
16261             })
16262         }
16263         
16264         return cfg;
16265         
16266     },
16267     inputEl: function ()
16268     {
16269         return this.el.select('input.roo-radio',true).first();
16270     },
16271     onClick : function()
16272     {   
16273         this.setChecked(true);
16274     },
16275     
16276     setChecked : function(state,suppressEvent)
16277     {
16278         if(state){
16279             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16280                 v.dom.checked = false;
16281             });
16282         }
16283         
16284         this.checked = state;
16285         this.inputEl().dom.checked = state;
16286         
16287         if(suppressEvent !== true){
16288             this.fireEvent('check', this, state);
16289         }
16290         
16291         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16292         
16293     },
16294     
16295     getGroupValue : function()
16296     {
16297         var value = ''
16298         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16299             if(v.dom.checked == true){
16300                 value = v.dom.value;
16301             }
16302         });
16303         
16304         return value;
16305     },
16306     
16307     /**
16308      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16309      * @return {Mixed} value The field value
16310      */
16311     getValue : function(){
16312         return this.getGroupValue();
16313     }
16314     
16315 });
16316
16317  
16318 //<script type="text/javascript">
16319
16320 /*
16321  * Based  Ext JS Library 1.1.1
16322  * Copyright(c) 2006-2007, Ext JS, LLC.
16323  * LGPL
16324  *
16325  */
16326  
16327 /**
16328  * @class Roo.HtmlEditorCore
16329  * @extends Roo.Component
16330  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16331  *
16332  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16333  */
16334
16335 Roo.HtmlEditorCore = function(config){
16336     
16337     
16338     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16339     
16340     
16341     this.addEvents({
16342         /**
16343          * @event initialize
16344          * Fires when the editor is fully initialized (including the iframe)
16345          * @param {Roo.HtmlEditorCore} this
16346          */
16347         initialize: true,
16348         /**
16349          * @event activate
16350          * Fires when the editor is first receives the focus. Any insertion must wait
16351          * until after this event.
16352          * @param {Roo.HtmlEditorCore} this
16353          */
16354         activate: true,
16355          /**
16356          * @event beforesync
16357          * Fires before the textarea is updated with content from the editor iframe. Return false
16358          * to cancel the sync.
16359          * @param {Roo.HtmlEditorCore} this
16360          * @param {String} html
16361          */
16362         beforesync: true,
16363          /**
16364          * @event beforepush
16365          * Fires before the iframe editor is updated with content from the textarea. Return false
16366          * to cancel the push.
16367          * @param {Roo.HtmlEditorCore} this
16368          * @param {String} html
16369          */
16370         beforepush: true,
16371          /**
16372          * @event sync
16373          * Fires when the textarea is updated with content from the editor iframe.
16374          * @param {Roo.HtmlEditorCore} this
16375          * @param {String} html
16376          */
16377         sync: true,
16378          /**
16379          * @event push
16380          * Fires when the iframe editor is updated with content from the textarea.
16381          * @param {Roo.HtmlEditorCore} this
16382          * @param {String} html
16383          */
16384         push: true,
16385         
16386         /**
16387          * @event editorevent
16388          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16389          * @param {Roo.HtmlEditorCore} this
16390          */
16391         editorevent: true
16392     });
16393     
16394     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16395     
16396     // defaults : white / black...
16397     this.applyBlacklists();
16398     
16399     
16400     
16401 };
16402
16403
16404 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16405
16406
16407      /**
16408      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16409      */
16410     
16411     owner : false,
16412     
16413      /**
16414      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16415      *                        Roo.resizable.
16416      */
16417     resizable : false,
16418      /**
16419      * @cfg {Number} height (in pixels)
16420      */   
16421     height: 300,
16422    /**
16423      * @cfg {Number} width (in pixels)
16424      */   
16425     width: 500,
16426     
16427     /**
16428      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16429      * 
16430      */
16431     stylesheets: false,
16432     
16433     // id of frame..
16434     frameId: false,
16435     
16436     // private properties
16437     validationEvent : false,
16438     deferHeight: true,
16439     initialized : false,
16440     activated : false,
16441     sourceEditMode : false,
16442     onFocus : Roo.emptyFn,
16443     iframePad:3,
16444     hideMode:'offsets',
16445     
16446     clearUp: true,
16447     
16448     // blacklist + whitelisted elements..
16449     black: false,
16450     white: false,
16451      
16452     
16453
16454     /**
16455      * Protected method that will not generally be called directly. It
16456      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16457      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16458      */
16459     getDocMarkup : function(){
16460         // body styles..
16461         var st = '';
16462         Roo.log(this.stylesheets);
16463         
16464         // inherit styels from page...?? 
16465         if (this.stylesheets === false) {
16466             
16467             Roo.get(document.head).select('style').each(function(node) {
16468                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16469             });
16470             
16471             Roo.get(document.head).select('link').each(function(node) { 
16472                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16473             });
16474             
16475         } else if (!this.stylesheets.length) {
16476                 // simple..
16477                 st = '<style type="text/css">' +
16478                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16479                    '</style>';
16480         } else {
16481             Roo.each(this.stylesheets, function(s) {
16482                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16483             });
16484             
16485         }
16486         
16487         st +=  '<style type="text/css">' +
16488             'IMG { cursor: pointer } ' +
16489         '</style>';
16490
16491         
16492         return '<html><head>' + st  +
16493             //<style type="text/css">' +
16494             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16495             //'</style>' +
16496             ' </head><body class="roo-htmleditor-body"></body></html>';
16497     },
16498
16499     // private
16500     onRender : function(ct, position)
16501     {
16502         var _t = this;
16503         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16504         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16505         
16506         
16507         this.el.dom.style.border = '0 none';
16508         this.el.dom.setAttribute('tabIndex', -1);
16509         this.el.addClass('x-hidden hide');
16510         
16511         
16512         
16513         if(Roo.isIE){ // fix IE 1px bogus margin
16514             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16515         }
16516        
16517         
16518         this.frameId = Roo.id();
16519         
16520          
16521         
16522         var iframe = this.owner.wrap.createChild({
16523             tag: 'iframe',
16524             cls: 'form-control', // bootstrap..
16525             id: this.frameId,
16526             name: this.frameId,
16527             frameBorder : 'no',
16528             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16529         }, this.el
16530         );
16531         
16532         
16533         this.iframe = iframe.dom;
16534
16535          this.assignDocWin();
16536         
16537         this.doc.designMode = 'on';
16538        
16539         this.doc.open();
16540         this.doc.write(this.getDocMarkup());
16541         this.doc.close();
16542
16543         
16544         var task = { // must defer to wait for browser to be ready
16545             run : function(){
16546                 //console.log("run task?" + this.doc.readyState);
16547                 this.assignDocWin();
16548                 if(this.doc.body || this.doc.readyState == 'complete'){
16549                     try {
16550                         this.doc.designMode="on";
16551                     } catch (e) {
16552                         return;
16553                     }
16554                     Roo.TaskMgr.stop(task);
16555                     this.initEditor.defer(10, this);
16556                 }
16557             },
16558             interval : 10,
16559             duration: 10000,
16560             scope: this
16561         };
16562         Roo.TaskMgr.start(task);
16563
16564         
16565          
16566     },
16567
16568     // private
16569     onResize : function(w, h)
16570     {
16571          Roo.log('resize: ' +w + ',' + h );
16572         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16573         if(!this.iframe){
16574             return;
16575         }
16576         if(typeof w == 'number'){
16577             
16578             this.iframe.style.width = w + 'px';
16579         }
16580         if(typeof h == 'number'){
16581             
16582             this.iframe.style.height = h + 'px';
16583             if(this.doc){
16584                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16585             }
16586         }
16587         
16588     },
16589
16590     /**
16591      * Toggles the editor between standard and source edit mode.
16592      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16593      */
16594     toggleSourceEdit : function(sourceEditMode){
16595         
16596         this.sourceEditMode = sourceEditMode === true;
16597         
16598         if(this.sourceEditMode){
16599  
16600             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16601             
16602         }else{
16603             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16604             //this.iframe.className = '';
16605             this.deferFocus();
16606         }
16607         //this.setSize(this.owner.wrap.getSize());
16608         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16609     },
16610
16611     
16612   
16613
16614     /**
16615      * Protected method that will not generally be called directly. If you need/want
16616      * custom HTML cleanup, this is the method you should override.
16617      * @param {String} html The HTML to be cleaned
16618      * return {String} The cleaned HTML
16619      */
16620     cleanHtml : function(html){
16621         html = String(html);
16622         if(html.length > 5){
16623             if(Roo.isSafari){ // strip safari nonsense
16624                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16625             }
16626         }
16627         if(html == '&nbsp;'){
16628             html = '';
16629         }
16630         return html;
16631     },
16632
16633     /**
16634      * HTML Editor -> Textarea
16635      * Protected method that will not generally be called directly. Syncs the contents
16636      * of the editor iframe with the textarea.
16637      */
16638     syncValue : function(){
16639         if(this.initialized){
16640             var bd = (this.doc.body || this.doc.documentElement);
16641             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16642             var html = bd.innerHTML;
16643             if(Roo.isSafari){
16644                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16645                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16646                 if(m && m[1]){
16647                     html = '<div style="'+m[0]+'">' + html + '</div>';
16648                 }
16649             }
16650             html = this.cleanHtml(html);
16651             // fix up the special chars.. normaly like back quotes in word...
16652             // however we do not want to do this with chinese..
16653             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16654                 var cc = b.charCodeAt();
16655                 if (
16656                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16657                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16658                     (cc >= 0xf900 && cc < 0xfb00 )
16659                 ) {
16660                         return b;
16661                 }
16662                 return "&#"+cc+";" 
16663             });
16664             if(this.owner.fireEvent('beforesync', this, html) !== false){
16665                 this.el.dom.value = html;
16666                 this.owner.fireEvent('sync', this, html);
16667             }
16668         }
16669     },
16670
16671     /**
16672      * Protected method that will not generally be called directly. Pushes the value of the textarea
16673      * into the iframe editor.
16674      */
16675     pushValue : function(){
16676         if(this.initialized){
16677             var v = this.el.dom.value.trim();
16678             
16679 //            if(v.length < 1){
16680 //                v = '&#160;';
16681 //            }
16682             
16683             if(this.owner.fireEvent('beforepush', this, v) !== false){
16684                 var d = (this.doc.body || this.doc.documentElement);
16685                 d.innerHTML = v;
16686                 this.cleanUpPaste();
16687                 this.el.dom.value = d.innerHTML;
16688                 this.owner.fireEvent('push', this, v);
16689             }
16690         }
16691     },
16692
16693     // private
16694     deferFocus : function(){
16695         this.focus.defer(10, this);
16696     },
16697
16698     // doc'ed in Field
16699     focus : function(){
16700         if(this.win && !this.sourceEditMode){
16701             this.win.focus();
16702         }else{
16703             this.el.focus();
16704         }
16705     },
16706     
16707     assignDocWin: function()
16708     {
16709         var iframe = this.iframe;
16710         
16711          if(Roo.isIE){
16712             this.doc = iframe.contentWindow.document;
16713             this.win = iframe.contentWindow;
16714         } else {
16715 //            if (!Roo.get(this.frameId)) {
16716 //                return;
16717 //            }
16718 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16719 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16720             
16721             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16722                 return;
16723             }
16724             
16725             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16726             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16727         }
16728     },
16729     
16730     // private
16731     initEditor : function(){
16732         //console.log("INIT EDITOR");
16733         this.assignDocWin();
16734         
16735         
16736         
16737         this.doc.designMode="on";
16738         this.doc.open();
16739         this.doc.write(this.getDocMarkup());
16740         this.doc.close();
16741         
16742         var dbody = (this.doc.body || this.doc.documentElement);
16743         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16744         // this copies styles from the containing element into thsi one..
16745         // not sure why we need all of this..
16746         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16747         
16748         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16749         //ss['background-attachment'] = 'fixed'; // w3c
16750         dbody.bgProperties = 'fixed'; // ie
16751         //Roo.DomHelper.applyStyles(dbody, ss);
16752         Roo.EventManager.on(this.doc, {
16753             //'mousedown': this.onEditorEvent,
16754             'mouseup': this.onEditorEvent,
16755             'dblclick': this.onEditorEvent,
16756             'click': this.onEditorEvent,
16757             'keyup': this.onEditorEvent,
16758             buffer:100,
16759             scope: this
16760         });
16761         if(Roo.isGecko){
16762             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16763         }
16764         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16765             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16766         }
16767         this.initialized = true;
16768
16769         this.owner.fireEvent('initialize', this);
16770         this.pushValue();
16771     },
16772
16773     // private
16774     onDestroy : function(){
16775         
16776         
16777         
16778         if(this.rendered){
16779             
16780             //for (var i =0; i < this.toolbars.length;i++) {
16781             //    // fixme - ask toolbars for heights?
16782             //    this.toolbars[i].onDestroy();
16783            // }
16784             
16785             //this.wrap.dom.innerHTML = '';
16786             //this.wrap.remove();
16787         }
16788     },
16789
16790     // private
16791     onFirstFocus : function(){
16792         
16793         this.assignDocWin();
16794         
16795         
16796         this.activated = true;
16797          
16798     
16799         if(Roo.isGecko){ // prevent silly gecko errors
16800             this.win.focus();
16801             var s = this.win.getSelection();
16802             if(!s.focusNode || s.focusNode.nodeType != 3){
16803                 var r = s.getRangeAt(0);
16804                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16805                 r.collapse(true);
16806                 this.deferFocus();
16807             }
16808             try{
16809                 this.execCmd('useCSS', true);
16810                 this.execCmd('styleWithCSS', false);
16811             }catch(e){}
16812         }
16813         this.owner.fireEvent('activate', this);
16814     },
16815
16816     // private
16817     adjustFont: function(btn){
16818         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16819         //if(Roo.isSafari){ // safari
16820         //    adjust *= 2;
16821        // }
16822         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16823         if(Roo.isSafari){ // safari
16824             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16825             v =  (v < 10) ? 10 : v;
16826             v =  (v > 48) ? 48 : v;
16827             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16828             
16829         }
16830         
16831         
16832         v = Math.max(1, v+adjust);
16833         
16834         this.execCmd('FontSize', v  );
16835     },
16836
16837     onEditorEvent : function(e){
16838         this.owner.fireEvent('editorevent', this, e);
16839       //  this.updateToolbar();
16840         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16841     },
16842
16843     insertTag : function(tg)
16844     {
16845         // could be a bit smarter... -> wrap the current selected tRoo..
16846         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16847             
16848             range = this.createRange(this.getSelection());
16849             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16850             wrappingNode.appendChild(range.extractContents());
16851             range.insertNode(wrappingNode);
16852
16853             return;
16854             
16855             
16856             
16857         }
16858         this.execCmd("formatblock",   tg);
16859         
16860     },
16861     
16862     insertText : function(txt)
16863     {
16864         
16865         
16866         var range = this.createRange();
16867         range.deleteContents();
16868                //alert(Sender.getAttribute('label'));
16869                
16870         range.insertNode(this.doc.createTextNode(txt));
16871     } ,
16872     
16873      
16874
16875     /**
16876      * Executes a Midas editor command on the editor document and performs necessary focus and
16877      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16878      * @param {String} cmd The Midas command
16879      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16880      */
16881     relayCmd : function(cmd, value){
16882         this.win.focus();
16883         this.execCmd(cmd, value);
16884         this.owner.fireEvent('editorevent', this);
16885         //this.updateToolbar();
16886         this.owner.deferFocus();
16887     },
16888
16889     /**
16890      * Executes a Midas editor command directly on the editor document.
16891      * For visual commands, you should use {@link #relayCmd} instead.
16892      * <b>This should only be called after the editor is initialized.</b>
16893      * @param {String} cmd The Midas command
16894      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16895      */
16896     execCmd : function(cmd, value){
16897         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16898         this.syncValue();
16899     },
16900  
16901  
16902    
16903     /**
16904      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16905      * to insert tRoo.
16906      * @param {String} text | dom node.. 
16907      */
16908     insertAtCursor : function(text)
16909     {
16910         
16911         
16912         
16913         if(!this.activated){
16914             return;
16915         }
16916         /*
16917         if(Roo.isIE){
16918             this.win.focus();
16919             var r = this.doc.selection.createRange();
16920             if(r){
16921                 r.collapse(true);
16922                 r.pasteHTML(text);
16923                 this.syncValue();
16924                 this.deferFocus();
16925             
16926             }
16927             return;
16928         }
16929         */
16930         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16931             this.win.focus();
16932             
16933             
16934             // from jquery ui (MIT licenced)
16935             var range, node;
16936             var win = this.win;
16937             
16938             if (win.getSelection && win.getSelection().getRangeAt) {
16939                 range = win.getSelection().getRangeAt(0);
16940                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16941                 range.insertNode(node);
16942             } else if (win.document.selection && win.document.selection.createRange) {
16943                 // no firefox support
16944                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16945                 win.document.selection.createRange().pasteHTML(txt);
16946             } else {
16947                 // no firefox support
16948                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16949                 this.execCmd('InsertHTML', txt);
16950             } 
16951             
16952             this.syncValue();
16953             
16954             this.deferFocus();
16955         }
16956     },
16957  // private
16958     mozKeyPress : function(e){
16959         if(e.ctrlKey){
16960             var c = e.getCharCode(), cmd;
16961           
16962             if(c > 0){
16963                 c = String.fromCharCode(c).toLowerCase();
16964                 switch(c){
16965                     case 'b':
16966                         cmd = 'bold';
16967                         break;
16968                     case 'i':
16969                         cmd = 'italic';
16970                         break;
16971                     
16972                     case 'u':
16973                         cmd = 'underline';
16974                         break;
16975                     
16976                     case 'v':
16977                         this.cleanUpPaste.defer(100, this);
16978                         return;
16979                         
16980                 }
16981                 if(cmd){
16982                     this.win.focus();
16983                     this.execCmd(cmd);
16984                     this.deferFocus();
16985                     e.preventDefault();
16986                 }
16987                 
16988             }
16989         }
16990     },
16991
16992     // private
16993     fixKeys : function(){ // load time branching for fastest keydown performance
16994         if(Roo.isIE){
16995             return function(e){
16996                 var k = e.getKey(), r;
16997                 if(k == e.TAB){
16998                     e.stopEvent();
16999                     r = this.doc.selection.createRange();
17000                     if(r){
17001                         r.collapse(true);
17002                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17003                         this.deferFocus();
17004                     }
17005                     return;
17006                 }
17007                 
17008                 if(k == e.ENTER){
17009                     r = this.doc.selection.createRange();
17010                     if(r){
17011                         var target = r.parentElement();
17012                         if(!target || target.tagName.toLowerCase() != 'li'){
17013                             e.stopEvent();
17014                             r.pasteHTML('<br />');
17015                             r.collapse(false);
17016                             r.select();
17017                         }
17018                     }
17019                 }
17020                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17021                     this.cleanUpPaste.defer(100, this);
17022                     return;
17023                 }
17024                 
17025                 
17026             };
17027         }else if(Roo.isOpera){
17028             return function(e){
17029                 var k = e.getKey();
17030                 if(k == e.TAB){
17031                     e.stopEvent();
17032                     this.win.focus();
17033                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17034                     this.deferFocus();
17035                 }
17036                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17037                     this.cleanUpPaste.defer(100, this);
17038                     return;
17039                 }
17040                 
17041             };
17042         }else if(Roo.isSafari){
17043             return function(e){
17044                 var k = e.getKey();
17045                 
17046                 if(k == e.TAB){
17047                     e.stopEvent();
17048                     this.execCmd('InsertText','\t');
17049                     this.deferFocus();
17050                     return;
17051                 }
17052                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17053                     this.cleanUpPaste.defer(100, this);
17054                     return;
17055                 }
17056                 
17057              };
17058         }
17059     }(),
17060     
17061     getAllAncestors: function()
17062     {
17063         var p = this.getSelectedNode();
17064         var a = [];
17065         if (!p) {
17066             a.push(p); // push blank onto stack..
17067             p = this.getParentElement();
17068         }
17069         
17070         
17071         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17072             a.push(p);
17073             p = p.parentNode;
17074         }
17075         a.push(this.doc.body);
17076         return a;
17077     },
17078     lastSel : false,
17079     lastSelNode : false,
17080     
17081     
17082     getSelection : function() 
17083     {
17084         this.assignDocWin();
17085         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17086     },
17087     
17088     getSelectedNode: function() 
17089     {
17090         // this may only work on Gecko!!!
17091         
17092         // should we cache this!!!!
17093         
17094         
17095         
17096          
17097         var range = this.createRange(this.getSelection()).cloneRange();
17098         
17099         if (Roo.isIE) {
17100             var parent = range.parentElement();
17101             while (true) {
17102                 var testRange = range.duplicate();
17103                 testRange.moveToElementText(parent);
17104                 if (testRange.inRange(range)) {
17105                     break;
17106                 }
17107                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17108                     break;
17109                 }
17110                 parent = parent.parentElement;
17111             }
17112             return parent;
17113         }
17114         
17115         // is ancestor a text element.
17116         var ac =  range.commonAncestorContainer;
17117         if (ac.nodeType == 3) {
17118             ac = ac.parentNode;
17119         }
17120         
17121         var ar = ac.childNodes;
17122          
17123         var nodes = [];
17124         var other_nodes = [];
17125         var has_other_nodes = false;
17126         for (var i=0;i<ar.length;i++) {
17127             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17128                 continue;
17129             }
17130             // fullly contained node.
17131             
17132             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17133                 nodes.push(ar[i]);
17134                 continue;
17135             }
17136             
17137             // probably selected..
17138             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17139                 other_nodes.push(ar[i]);
17140                 continue;
17141             }
17142             // outer..
17143             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17144                 continue;
17145             }
17146             
17147             
17148             has_other_nodes = true;
17149         }
17150         if (!nodes.length && other_nodes.length) {
17151             nodes= other_nodes;
17152         }
17153         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17154             return false;
17155         }
17156         
17157         return nodes[0];
17158     },
17159     createRange: function(sel)
17160     {
17161         // this has strange effects when using with 
17162         // top toolbar - not sure if it's a great idea.
17163         //this.editor.contentWindow.focus();
17164         if (typeof sel != "undefined") {
17165             try {
17166                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17167             } catch(e) {
17168                 return this.doc.createRange();
17169             }
17170         } else {
17171             return this.doc.createRange();
17172         }
17173     },
17174     getParentElement: function()
17175     {
17176         
17177         this.assignDocWin();
17178         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17179         
17180         var range = this.createRange(sel);
17181          
17182         try {
17183             var p = range.commonAncestorContainer;
17184             while (p.nodeType == 3) { // text node
17185                 p = p.parentNode;
17186             }
17187             return p;
17188         } catch (e) {
17189             return null;
17190         }
17191     
17192     },
17193     /***
17194      *
17195      * Range intersection.. the hard stuff...
17196      *  '-1' = before
17197      *  '0' = hits..
17198      *  '1' = after.
17199      *         [ -- selected range --- ]
17200      *   [fail]                        [fail]
17201      *
17202      *    basically..
17203      *      if end is before start or  hits it. fail.
17204      *      if start is after end or hits it fail.
17205      *
17206      *   if either hits (but other is outside. - then it's not 
17207      *   
17208      *    
17209      **/
17210     
17211     
17212     // @see http://www.thismuchiknow.co.uk/?p=64.
17213     rangeIntersectsNode : function(range, node)
17214     {
17215         var nodeRange = node.ownerDocument.createRange();
17216         try {
17217             nodeRange.selectNode(node);
17218         } catch (e) {
17219             nodeRange.selectNodeContents(node);
17220         }
17221     
17222         var rangeStartRange = range.cloneRange();
17223         rangeStartRange.collapse(true);
17224     
17225         var rangeEndRange = range.cloneRange();
17226         rangeEndRange.collapse(false);
17227     
17228         var nodeStartRange = nodeRange.cloneRange();
17229         nodeStartRange.collapse(true);
17230     
17231         var nodeEndRange = nodeRange.cloneRange();
17232         nodeEndRange.collapse(false);
17233     
17234         return rangeStartRange.compareBoundaryPoints(
17235                  Range.START_TO_START, nodeEndRange) == -1 &&
17236                rangeEndRange.compareBoundaryPoints(
17237                  Range.START_TO_START, nodeStartRange) == 1;
17238         
17239          
17240     },
17241     rangeCompareNode : function(range, node)
17242     {
17243         var nodeRange = node.ownerDocument.createRange();
17244         try {
17245             nodeRange.selectNode(node);
17246         } catch (e) {
17247             nodeRange.selectNodeContents(node);
17248         }
17249         
17250         
17251         range.collapse(true);
17252     
17253         nodeRange.collapse(true);
17254      
17255         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17256         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17257          
17258         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17259         
17260         var nodeIsBefore   =  ss == 1;
17261         var nodeIsAfter    = ee == -1;
17262         
17263         if (nodeIsBefore && nodeIsAfter)
17264             return 0; // outer
17265         if (!nodeIsBefore && nodeIsAfter)
17266             return 1; //right trailed.
17267         
17268         if (nodeIsBefore && !nodeIsAfter)
17269             return 2;  // left trailed.
17270         // fully contined.
17271         return 3;
17272     },
17273
17274     // private? - in a new class?
17275     cleanUpPaste :  function()
17276     {
17277         // cleans up the whole document..
17278         Roo.log('cleanuppaste');
17279         
17280         this.cleanUpChildren(this.doc.body);
17281         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17282         if (clean != this.doc.body.innerHTML) {
17283             this.doc.body.innerHTML = clean;
17284         }
17285         
17286     },
17287     
17288     cleanWordChars : function(input) {// change the chars to hex code
17289         var he = Roo.HtmlEditorCore;
17290         
17291         var output = input;
17292         Roo.each(he.swapCodes, function(sw) { 
17293             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17294             
17295             output = output.replace(swapper, sw[1]);
17296         });
17297         
17298         return output;
17299     },
17300     
17301     
17302     cleanUpChildren : function (n)
17303     {
17304         if (!n.childNodes.length) {
17305             return;
17306         }
17307         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17308            this.cleanUpChild(n.childNodes[i]);
17309         }
17310     },
17311     
17312     
17313         
17314     
17315     cleanUpChild : function (node)
17316     {
17317         var ed = this;
17318         //console.log(node);
17319         if (node.nodeName == "#text") {
17320             // clean up silly Windows -- stuff?
17321             return; 
17322         }
17323         if (node.nodeName == "#comment") {
17324             node.parentNode.removeChild(node);
17325             // clean up silly Windows -- stuff?
17326             return; 
17327         }
17328         var lcname = node.tagName.toLowerCase();
17329         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17330         // whitelist of tags..
17331         
17332         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17333             // remove node.
17334             node.parentNode.removeChild(node);
17335             return;
17336             
17337         }
17338         
17339         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17340         
17341         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17342         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17343         
17344         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17345         //    remove_keep_children = true;
17346         //}
17347         
17348         if (remove_keep_children) {
17349             this.cleanUpChildren(node);
17350             // inserts everything just before this node...
17351             while (node.childNodes.length) {
17352                 var cn = node.childNodes[0];
17353                 node.removeChild(cn);
17354                 node.parentNode.insertBefore(cn, node);
17355             }
17356             node.parentNode.removeChild(node);
17357             return;
17358         }
17359         
17360         if (!node.attributes || !node.attributes.length) {
17361             this.cleanUpChildren(node);
17362             return;
17363         }
17364         
17365         function cleanAttr(n,v)
17366         {
17367             
17368             if (v.match(/^\./) || v.match(/^\//)) {
17369                 return;
17370             }
17371             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17372                 return;
17373             }
17374             if (v.match(/^#/)) {
17375                 return;
17376             }
17377 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17378             node.removeAttribute(n);
17379             
17380         }
17381         
17382         var cwhite = this.cwhite;
17383         var cblack = this.cblack;
17384             
17385         function cleanStyle(n,v)
17386         {
17387             if (v.match(/expression/)) { //XSS?? should we even bother..
17388                 node.removeAttribute(n);
17389                 return;
17390             }
17391             
17392             var parts = v.split(/;/);
17393             var clean = [];
17394             
17395             Roo.each(parts, function(p) {
17396                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17397                 if (!p.length) {
17398                     return true;
17399                 }
17400                 var l = p.split(':').shift().replace(/\s+/g,'');
17401                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17402                 
17403                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17404 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17405                     //node.removeAttribute(n);
17406                     return true;
17407                 }
17408                 //Roo.log()
17409                 // only allow 'c whitelisted system attributes'
17410                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17411 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17412                     //node.removeAttribute(n);
17413                     return true;
17414                 }
17415                 
17416                 
17417                  
17418                 
17419                 clean.push(p);
17420                 return true;
17421             });
17422             if (clean.length) { 
17423                 node.setAttribute(n, clean.join(';'));
17424             } else {
17425                 node.removeAttribute(n);
17426             }
17427             
17428         }
17429         
17430         
17431         for (var i = node.attributes.length-1; i > -1 ; i--) {
17432             var a = node.attributes[i];
17433             //console.log(a);
17434             
17435             if (a.name.toLowerCase().substr(0,2)=='on')  {
17436                 node.removeAttribute(a.name);
17437                 continue;
17438             }
17439             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17440                 node.removeAttribute(a.name);
17441                 continue;
17442             }
17443             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17444                 cleanAttr(a.name,a.value); // fixme..
17445                 continue;
17446             }
17447             if (a.name == 'style') {
17448                 cleanStyle(a.name,a.value);
17449                 continue;
17450             }
17451             /// clean up MS crap..
17452             // tecnically this should be a list of valid class'es..
17453             
17454             
17455             if (a.name == 'class') {
17456                 if (a.value.match(/^Mso/)) {
17457                     node.className = '';
17458                 }
17459                 
17460                 if (a.value.match(/body/)) {
17461                     node.className = '';
17462                 }
17463                 continue;
17464             }
17465             
17466             // style cleanup!?
17467             // class cleanup?
17468             
17469         }
17470         
17471         
17472         this.cleanUpChildren(node);
17473         
17474         
17475     },
17476     /**
17477      * Clean up MS wordisms...
17478      */
17479     cleanWord : function(node)
17480     {
17481         var _t = this;
17482         var cleanWordChildren = function()
17483         {
17484             if (!node.childNodes.length) {
17485                 return;
17486             }
17487             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17488                _t.cleanWord(node.childNodes[i]);
17489             }
17490         }
17491         
17492         
17493         if (!node) {
17494             this.cleanWord(this.doc.body);
17495             return;
17496         }
17497         if (node.nodeName == "#text") {
17498             // clean up silly Windows -- stuff?
17499             return; 
17500         }
17501         if (node.nodeName == "#comment") {
17502             node.parentNode.removeChild(node);
17503             // clean up silly Windows -- stuff?
17504             return; 
17505         }
17506         
17507         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17508             node.parentNode.removeChild(node);
17509             return;
17510         }
17511         
17512         // remove - but keep children..
17513         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17514             while (node.childNodes.length) {
17515                 var cn = node.childNodes[0];
17516                 node.removeChild(cn);
17517                 node.parentNode.insertBefore(cn, node);
17518             }
17519             node.parentNode.removeChild(node);
17520             cleanWordChildren();
17521             return;
17522         }
17523         // clean styles
17524         if (node.className.length) {
17525             
17526             var cn = node.className.split(/\W+/);
17527             var cna = [];
17528             Roo.each(cn, function(cls) {
17529                 if (cls.match(/Mso[a-zA-Z]+/)) {
17530                     return;
17531                 }
17532                 cna.push(cls);
17533             });
17534             node.className = cna.length ? cna.join(' ') : '';
17535             if (!cna.length) {
17536                 node.removeAttribute("class");
17537             }
17538         }
17539         
17540         if (node.hasAttribute("lang")) {
17541             node.removeAttribute("lang");
17542         }
17543         
17544         if (node.hasAttribute("style")) {
17545             
17546             var styles = node.getAttribute("style").split(";");
17547             var nstyle = [];
17548             Roo.each(styles, function(s) {
17549                 if (!s.match(/:/)) {
17550                     return;
17551                 }
17552                 var kv = s.split(":");
17553                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17554                     return;
17555                 }
17556                 // what ever is left... we allow.
17557                 nstyle.push(s);
17558             });
17559             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17560             if (!nstyle.length) {
17561                 node.removeAttribute('style');
17562             }
17563         }
17564         
17565         cleanWordChildren();
17566         
17567         
17568     },
17569     domToHTML : function(currentElement, depth, nopadtext) {
17570         
17571         depth = depth || 0;
17572         nopadtext = nopadtext || false;
17573     
17574         if (!currentElement) {
17575             return this.domToHTML(this.doc.body);
17576         }
17577         
17578         //Roo.log(currentElement);
17579         var j;
17580         var allText = false;
17581         var nodeName = currentElement.nodeName;
17582         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17583         
17584         if  (nodeName == '#text') {
17585             return currentElement.nodeValue;
17586         }
17587         
17588         
17589         var ret = '';
17590         if (nodeName != 'BODY') {
17591              
17592             var i = 0;
17593             // Prints the node tagName, such as <A>, <IMG>, etc
17594             if (tagName) {
17595                 var attr = [];
17596                 for(i = 0; i < currentElement.attributes.length;i++) {
17597                     // quoting?
17598                     var aname = currentElement.attributes.item(i).name;
17599                     if (!currentElement.attributes.item(i).value.length) {
17600                         continue;
17601                     }
17602                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17603                 }
17604                 
17605                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17606             } 
17607             else {
17608                 
17609                 // eack
17610             }
17611         } else {
17612             tagName = false;
17613         }
17614         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17615             return ret;
17616         }
17617         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17618             nopadtext = true;
17619         }
17620         
17621         
17622         // Traverse the tree
17623         i = 0;
17624         var currentElementChild = currentElement.childNodes.item(i);
17625         var allText = true;
17626         var innerHTML  = '';
17627         lastnode = '';
17628         while (currentElementChild) {
17629             // Formatting code (indent the tree so it looks nice on the screen)
17630             var nopad = nopadtext;
17631             if (lastnode == 'SPAN') {
17632                 nopad  = true;
17633             }
17634             // text
17635             if  (currentElementChild.nodeName == '#text') {
17636                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17637                 if (!nopad && toadd.length > 80) {
17638                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17639                 }
17640                 innerHTML  += toadd;
17641                 
17642                 i++;
17643                 currentElementChild = currentElement.childNodes.item(i);
17644                 lastNode = '';
17645                 continue;
17646             }
17647             allText = false;
17648             
17649             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17650                 
17651             // Recursively traverse the tree structure of the child node
17652             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17653             lastnode = currentElementChild.nodeName;
17654             i++;
17655             currentElementChild=currentElement.childNodes.item(i);
17656         }
17657         
17658         ret += innerHTML;
17659         
17660         if (!allText) {
17661                 // The remaining code is mostly for formatting the tree
17662             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17663         }
17664         
17665         
17666         if (tagName) {
17667             ret+= "</"+tagName+">";
17668         }
17669         return ret;
17670         
17671     },
17672         
17673     applyBlacklists : function()
17674     {
17675         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17676         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17677         
17678         this.white = [];
17679         this.black = [];
17680         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17681             if (b.indexOf(tag) > -1) {
17682                 return;
17683             }
17684             this.white.push(tag);
17685             
17686         }, this);
17687         
17688         Roo.each(w, function(tag) {
17689             if (b.indexOf(tag) > -1) {
17690                 return;
17691             }
17692             if (this.white.indexOf(tag) > -1) {
17693                 return;
17694             }
17695             this.white.push(tag);
17696             
17697         }, this);
17698         
17699         
17700         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17701             if (w.indexOf(tag) > -1) {
17702                 return;
17703             }
17704             this.black.push(tag);
17705             
17706         }, this);
17707         
17708         Roo.each(b, function(tag) {
17709             if (w.indexOf(tag) > -1) {
17710                 return;
17711             }
17712             if (this.black.indexOf(tag) > -1) {
17713                 return;
17714             }
17715             this.black.push(tag);
17716             
17717         }, this);
17718         
17719         
17720         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17721         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17722         
17723         this.cwhite = [];
17724         this.cblack = [];
17725         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17726             if (b.indexOf(tag) > -1) {
17727                 return;
17728             }
17729             this.cwhite.push(tag);
17730             
17731         }, this);
17732         
17733         Roo.each(w, function(tag) {
17734             if (b.indexOf(tag) > -1) {
17735                 return;
17736             }
17737             if (this.cwhite.indexOf(tag) > -1) {
17738                 return;
17739             }
17740             this.cwhite.push(tag);
17741             
17742         }, this);
17743         
17744         
17745         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17746             if (w.indexOf(tag) > -1) {
17747                 return;
17748             }
17749             this.cblack.push(tag);
17750             
17751         }, this);
17752         
17753         Roo.each(b, function(tag) {
17754             if (w.indexOf(tag) > -1) {
17755                 return;
17756             }
17757             if (this.cblack.indexOf(tag) > -1) {
17758                 return;
17759             }
17760             this.cblack.push(tag);
17761             
17762         }, this);
17763     }
17764     
17765     // hide stuff that is not compatible
17766     /**
17767      * @event blur
17768      * @hide
17769      */
17770     /**
17771      * @event change
17772      * @hide
17773      */
17774     /**
17775      * @event focus
17776      * @hide
17777      */
17778     /**
17779      * @event specialkey
17780      * @hide
17781      */
17782     /**
17783      * @cfg {String} fieldClass @hide
17784      */
17785     /**
17786      * @cfg {String} focusClass @hide
17787      */
17788     /**
17789      * @cfg {String} autoCreate @hide
17790      */
17791     /**
17792      * @cfg {String} inputType @hide
17793      */
17794     /**
17795      * @cfg {String} invalidClass @hide
17796      */
17797     /**
17798      * @cfg {String} invalidText @hide
17799      */
17800     /**
17801      * @cfg {String} msgFx @hide
17802      */
17803     /**
17804      * @cfg {String} validateOnBlur @hide
17805      */
17806 });
17807
17808 Roo.HtmlEditorCore.white = [
17809         'area', 'br', 'img', 'input', 'hr', 'wbr',
17810         
17811        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17812        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17813        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17814        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17815        'table',   'ul',         'xmp', 
17816        
17817        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17818       'thead',   'tr', 
17819      
17820       'dir', 'menu', 'ol', 'ul', 'dl',
17821        
17822       'embed',  'object'
17823 ];
17824
17825
17826 Roo.HtmlEditorCore.black = [
17827     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17828         'applet', // 
17829         'base',   'basefont', 'bgsound', 'blink',  'body', 
17830         'frame',  'frameset', 'head',    'html',   'ilayer', 
17831         'iframe', 'layer',  'link',     'meta',    'object',   
17832         'script', 'style' ,'title',  'xml' // clean later..
17833 ];
17834 Roo.HtmlEditorCore.clean = [
17835     'script', 'style', 'title', 'xml'
17836 ];
17837 Roo.HtmlEditorCore.remove = [
17838     'font'
17839 ];
17840 // attributes..
17841
17842 Roo.HtmlEditorCore.ablack = [
17843     'on'
17844 ];
17845     
17846 Roo.HtmlEditorCore.aclean = [ 
17847     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17848 ];
17849
17850 // protocols..
17851 Roo.HtmlEditorCore.pwhite= [
17852         'http',  'https',  'mailto'
17853 ];
17854
17855 // white listed style attributes.
17856 Roo.HtmlEditorCore.cwhite= [
17857       //  'text-align', /// default is to allow most things..
17858       
17859          
17860 //        'font-size'//??
17861 ];
17862
17863 // black listed style attributes.
17864 Roo.HtmlEditorCore.cblack= [
17865       //  'font-size' -- this can be set by the project 
17866 ];
17867
17868
17869 Roo.HtmlEditorCore.swapCodes   =[ 
17870     [    8211, "--" ], 
17871     [    8212, "--" ], 
17872     [    8216,  "'" ],  
17873     [    8217, "'" ],  
17874     [    8220, '"' ],  
17875     [    8221, '"' ],  
17876     [    8226, "*" ],  
17877     [    8230, "..." ]
17878 ]; 
17879
17880     /*
17881  * - LGPL
17882  *
17883  * HtmlEditor
17884  * 
17885  */
17886
17887 /**
17888  * @class Roo.bootstrap.HtmlEditor
17889  * @extends Roo.bootstrap.TextArea
17890  * Bootstrap HtmlEditor class
17891
17892  * @constructor
17893  * Create a new HtmlEditor
17894  * @param {Object} config The config object
17895  */
17896
17897 Roo.bootstrap.HtmlEditor = function(config){
17898     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17899     if (!this.toolbars) {
17900         this.toolbars = [];
17901     }
17902     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17903     this.addEvents({
17904             /**
17905              * @event initialize
17906              * Fires when the editor is fully initialized (including the iframe)
17907              * @param {HtmlEditor} this
17908              */
17909             initialize: true,
17910             /**
17911              * @event activate
17912              * Fires when the editor is first receives the focus. Any insertion must wait
17913              * until after this event.
17914              * @param {HtmlEditor} this
17915              */
17916             activate: true,
17917              /**
17918              * @event beforesync
17919              * Fires before the textarea is updated with content from the editor iframe. Return false
17920              * to cancel the sync.
17921              * @param {HtmlEditor} this
17922              * @param {String} html
17923              */
17924             beforesync: true,
17925              /**
17926              * @event beforepush
17927              * Fires before the iframe editor is updated with content from the textarea. Return false
17928              * to cancel the push.
17929              * @param {HtmlEditor} this
17930              * @param {String} html
17931              */
17932             beforepush: true,
17933              /**
17934              * @event sync
17935              * Fires when the textarea is updated with content from the editor iframe.
17936              * @param {HtmlEditor} this
17937              * @param {String} html
17938              */
17939             sync: true,
17940              /**
17941              * @event push
17942              * Fires when the iframe editor is updated with content from the textarea.
17943              * @param {HtmlEditor} this
17944              * @param {String} html
17945              */
17946             push: true,
17947              /**
17948              * @event editmodechange
17949              * Fires when the editor switches edit modes
17950              * @param {HtmlEditor} this
17951              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17952              */
17953             editmodechange: true,
17954             /**
17955              * @event editorevent
17956              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17957              * @param {HtmlEditor} this
17958              */
17959             editorevent: true,
17960             /**
17961              * @event firstfocus
17962              * Fires when on first focus - needed by toolbars..
17963              * @param {HtmlEditor} this
17964              */
17965             firstfocus: true,
17966             /**
17967              * @event autosave
17968              * Auto save the htmlEditor value as a file into Events
17969              * @param {HtmlEditor} this
17970              */
17971             autosave: true,
17972             /**
17973              * @event savedpreview
17974              * preview the saved version of htmlEditor
17975              * @param {HtmlEditor} this
17976              */
17977             savedpreview: true
17978         });
17979 };
17980
17981
17982 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17983     
17984     
17985       /**
17986      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17987      */
17988     toolbars : false,
17989    
17990      /**
17991      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17992      *                        Roo.resizable.
17993      */
17994     resizable : false,
17995      /**
17996      * @cfg {Number} height (in pixels)
17997      */   
17998     height: 300,
17999    /**
18000      * @cfg {Number} width (in pixels)
18001      */   
18002     width: false,
18003     
18004     /**
18005      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18006      * 
18007      */
18008     stylesheets: false,
18009     
18010     // id of frame..
18011     frameId: false,
18012     
18013     // private properties
18014     validationEvent : false,
18015     deferHeight: true,
18016     initialized : false,
18017     activated : false,
18018     
18019     onFocus : Roo.emptyFn,
18020     iframePad:3,
18021     hideMode:'offsets',
18022     
18023     
18024     tbContainer : false,
18025     
18026     toolbarContainer :function() {
18027         return this.wrap.select('.x-html-editor-tb',true).first();
18028     },
18029
18030     /**
18031      * Protected method that will not generally be called directly. It
18032      * is called when the editor creates its toolbar. Override this method if you need to
18033      * add custom toolbar buttons.
18034      * @param {HtmlEditor} editor
18035      */
18036     createToolbar : function(){
18037         
18038         Roo.log("create toolbars");
18039         
18040         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18041         this.toolbars[0].render(this.toolbarContainer());
18042         
18043         return;
18044         
18045 //        if (!editor.toolbars || !editor.toolbars.length) {
18046 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18047 //        }
18048 //        
18049 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18050 //            editor.toolbars[i] = Roo.factory(
18051 //                    typeof(editor.toolbars[i]) == 'string' ?
18052 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18053 //                Roo.bootstrap.HtmlEditor);
18054 //            editor.toolbars[i].init(editor);
18055 //        }
18056     },
18057
18058      
18059     // private
18060     onRender : function(ct, position)
18061     {
18062        // Roo.log("Call onRender: " + this.xtype);
18063         var _t = this;
18064         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18065       
18066         this.wrap = this.inputEl().wrap({
18067             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18068         });
18069         
18070         this.editorcore.onRender(ct, position);
18071          
18072         if (this.resizable) {
18073             this.resizeEl = new Roo.Resizable(this.wrap, {
18074                 pinned : true,
18075                 wrap: true,
18076                 dynamic : true,
18077                 minHeight : this.height,
18078                 height: this.height,
18079                 handles : this.resizable,
18080                 width: this.width,
18081                 listeners : {
18082                     resize : function(r, w, h) {
18083                         _t.onResize(w,h); // -something
18084                     }
18085                 }
18086             });
18087             
18088         }
18089         this.createToolbar(this);
18090        
18091         
18092         if(!this.width && this.resizable){
18093             this.setSize(this.wrap.getSize());
18094         }
18095         if (this.resizeEl) {
18096             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18097             // should trigger onReize..
18098         }
18099         
18100     },
18101
18102     // private
18103     onResize : function(w, h)
18104     {
18105         Roo.log('resize: ' +w + ',' + h );
18106         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18107         var ew = false;
18108         var eh = false;
18109         
18110         if(this.inputEl() ){
18111             if(typeof w == 'number'){
18112                 var aw = w - this.wrap.getFrameWidth('lr');
18113                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18114                 ew = aw;
18115             }
18116             if(typeof h == 'number'){
18117                  var tbh = -11;  // fixme it needs to tool bar size!
18118                 for (var i =0; i < this.toolbars.length;i++) {
18119                     // fixme - ask toolbars for heights?
18120                     tbh += this.toolbars[i].el.getHeight();
18121                     //if (this.toolbars[i].footer) {
18122                     //    tbh += this.toolbars[i].footer.el.getHeight();
18123                     //}
18124                 }
18125               
18126                 
18127                 
18128                 
18129                 
18130                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18131                 ah -= 5; // knock a few pixes off for look..
18132                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18133                 var eh = ah;
18134             }
18135         }
18136         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18137         this.editorcore.onResize(ew,eh);
18138         
18139     },
18140
18141     /**
18142      * Toggles the editor between standard and source edit mode.
18143      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18144      */
18145     toggleSourceEdit : function(sourceEditMode)
18146     {
18147         this.editorcore.toggleSourceEdit(sourceEditMode);
18148         
18149         if(this.editorcore.sourceEditMode){
18150             Roo.log('editor - showing textarea');
18151             
18152 //            Roo.log('in');
18153 //            Roo.log(this.syncValue());
18154             this.syncValue();
18155             this.inputEl().removeClass(['hide', 'x-hidden']);
18156             this.inputEl().dom.removeAttribute('tabIndex');
18157             this.inputEl().focus();
18158         }else{
18159             Roo.log('editor - hiding textarea');
18160 //            Roo.log('out')
18161 //            Roo.log(this.pushValue()); 
18162             this.pushValue();
18163             
18164             this.inputEl().addClass(['hide', 'x-hidden']);
18165             this.inputEl().dom.setAttribute('tabIndex', -1);
18166             //this.deferFocus();
18167         }
18168          
18169         if(this.resizable){
18170             this.setSize(this.wrap.getSize());
18171         }
18172         
18173         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18174     },
18175  
18176     // private (for BoxComponent)
18177     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18178
18179     // private (for BoxComponent)
18180     getResizeEl : function(){
18181         return this.wrap;
18182     },
18183
18184     // private (for BoxComponent)
18185     getPositionEl : function(){
18186         return this.wrap;
18187     },
18188
18189     // private
18190     initEvents : function(){
18191         this.originalValue = this.getValue();
18192     },
18193
18194 //    /**
18195 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18196 //     * @method
18197 //     */
18198 //    markInvalid : Roo.emptyFn,
18199 //    /**
18200 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18201 //     * @method
18202 //     */
18203 //    clearInvalid : Roo.emptyFn,
18204
18205     setValue : function(v){
18206         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18207         this.editorcore.pushValue();
18208     },
18209
18210      
18211     // private
18212     deferFocus : function(){
18213         this.focus.defer(10, this);
18214     },
18215
18216     // doc'ed in Field
18217     focus : function(){
18218         this.editorcore.focus();
18219         
18220     },
18221       
18222
18223     // private
18224     onDestroy : function(){
18225         
18226         
18227         
18228         if(this.rendered){
18229             
18230             for (var i =0; i < this.toolbars.length;i++) {
18231                 // fixme - ask toolbars for heights?
18232                 this.toolbars[i].onDestroy();
18233             }
18234             
18235             this.wrap.dom.innerHTML = '';
18236             this.wrap.remove();
18237         }
18238     },
18239
18240     // private
18241     onFirstFocus : function(){
18242         //Roo.log("onFirstFocus");
18243         this.editorcore.onFirstFocus();
18244          for (var i =0; i < this.toolbars.length;i++) {
18245             this.toolbars[i].onFirstFocus();
18246         }
18247         
18248     },
18249     
18250     // private
18251     syncValue : function()
18252     {   
18253         this.editorcore.syncValue();
18254     },
18255     
18256     pushValue : function()
18257     {   
18258         this.editorcore.pushValue();
18259     }
18260      
18261     
18262     // hide stuff that is not compatible
18263     /**
18264      * @event blur
18265      * @hide
18266      */
18267     /**
18268      * @event change
18269      * @hide
18270      */
18271     /**
18272      * @event focus
18273      * @hide
18274      */
18275     /**
18276      * @event specialkey
18277      * @hide
18278      */
18279     /**
18280      * @cfg {String} fieldClass @hide
18281      */
18282     /**
18283      * @cfg {String} focusClass @hide
18284      */
18285     /**
18286      * @cfg {String} autoCreate @hide
18287      */
18288     /**
18289      * @cfg {String} inputType @hide
18290      */
18291     /**
18292      * @cfg {String} invalidClass @hide
18293      */
18294     /**
18295      * @cfg {String} invalidText @hide
18296      */
18297     /**
18298      * @cfg {String} msgFx @hide
18299      */
18300     /**
18301      * @cfg {String} validateOnBlur @hide
18302      */
18303 });
18304  
18305     
18306    
18307    
18308    
18309       
18310 Roo.namespace('Roo.bootstrap.htmleditor');
18311 /**
18312  * @class Roo.bootstrap.HtmlEditorToolbar1
18313  * Basic Toolbar
18314  * 
18315  * Usage:
18316  *
18317  new Roo.bootstrap.HtmlEditor({
18318     ....
18319     toolbars : [
18320         new Roo.bootstrap.HtmlEditorToolbar1({
18321             disable : { fonts: 1 , format: 1, ..., ... , ...],
18322             btns : [ .... ]
18323         })
18324     }
18325      
18326  * 
18327  * @cfg {Object} disable List of elements to disable..
18328  * @cfg {Array} btns List of additional buttons.
18329  * 
18330  * 
18331  * NEEDS Extra CSS? 
18332  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18333  */
18334  
18335 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18336 {
18337     
18338     Roo.apply(this, config);
18339     
18340     // default disabled, based on 'good practice'..
18341     this.disable = this.disable || {};
18342     Roo.applyIf(this.disable, {
18343         fontSize : true,
18344         colors : true,
18345         specialElements : true
18346     });
18347     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18348     
18349     this.editor = config.editor;
18350     this.editorcore = config.editor.editorcore;
18351     
18352     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18353     
18354     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18355     // dont call parent... till later.
18356 }
18357 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18358      
18359     bar : true,
18360     
18361     editor : false,
18362     editorcore : false,
18363     
18364     
18365     formats : [
18366         "p" ,  
18367         "h1","h2","h3","h4","h5","h6", 
18368         "pre", "code", 
18369         "abbr", "acronym", "address", "cite", "samp", "var",
18370         'div','span'
18371     ],
18372     
18373     onRender : function(ct, position)
18374     {
18375        // Roo.log("Call onRender: " + this.xtype);
18376         
18377        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18378        Roo.log(this.el);
18379        this.el.dom.style.marginBottom = '0';
18380        var _this = this;
18381        var editorcore = this.editorcore;
18382        var editor= this.editor;
18383        
18384        var children = [];
18385        var btn = function(id,cmd , toggle, handler){
18386        
18387             var  event = toggle ? 'toggle' : 'click';
18388        
18389             var a = {
18390                 size : 'sm',
18391                 xtype: 'Button',
18392                 xns: Roo.bootstrap,
18393                 glyphicon : id,
18394                 cmd : id || cmd,
18395                 enableToggle:toggle !== false,
18396                 //html : 'submit'
18397                 pressed : toggle ? false : null,
18398                 listeners : {}
18399             }
18400             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18401                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18402             }
18403             children.push(a);
18404             return a;
18405        }
18406         
18407         var style = {
18408                 xtype: 'Button',
18409                 size : 'sm',
18410                 xns: Roo.bootstrap,
18411                 glyphicon : 'font',
18412                 //html : 'submit'
18413                 menu : {
18414                     xtype: 'Menu',
18415                     xns: Roo.bootstrap,
18416                     items:  []
18417                 }
18418         };
18419         Roo.each(this.formats, function(f) {
18420             style.menu.items.push({
18421                 xtype :'MenuItem',
18422                 xns: Roo.bootstrap,
18423                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18424                 tagname : f,
18425                 listeners : {
18426                     click : function()
18427                     {
18428                         editorcore.insertTag(this.tagname);
18429                         editor.focus();
18430                     }
18431                 }
18432                 
18433             });
18434         });
18435          children.push(style);   
18436             
18437             
18438         btn('bold',false,true);
18439         btn('italic',false,true);
18440         btn('align-left', 'justifyleft',true);
18441         btn('align-center', 'justifycenter',true);
18442         btn('align-right' , 'justifyright',true);
18443         btn('link', false, false, function(btn) {
18444             //Roo.log("create link?");
18445             var url = prompt(this.createLinkText, this.defaultLinkValue);
18446             if(url && url != 'http:/'+'/'){
18447                 this.editorcore.relayCmd('createlink', url);
18448             }
18449         }),
18450         btn('list','insertunorderedlist',true);
18451         btn('pencil', false,true, function(btn){
18452                 Roo.log(this);
18453                 
18454                 this.toggleSourceEdit(btn.pressed);
18455         });
18456         /*
18457         var cog = {
18458                 xtype: 'Button',
18459                 size : 'sm',
18460                 xns: Roo.bootstrap,
18461                 glyphicon : 'cog',
18462                 //html : 'submit'
18463                 menu : {
18464                     xtype: 'Menu',
18465                     xns: Roo.bootstrap,
18466                     items:  []
18467                 }
18468         };
18469         
18470         cog.menu.items.push({
18471             xtype :'MenuItem',
18472             xns: Roo.bootstrap,
18473             html : Clean styles,
18474             tagname : f,
18475             listeners : {
18476                 click : function()
18477                 {
18478                     editorcore.insertTag(this.tagname);
18479                     editor.focus();
18480                 }
18481             }
18482             
18483         });
18484        */
18485         
18486          
18487        this.xtype = 'NavSimplebar';
18488         
18489         for(var i=0;i< children.length;i++) {
18490             
18491             this.buttons.add(this.addxtypeChild(children[i]));
18492             
18493         }
18494         
18495         editor.on('editorevent', this.updateToolbar, this);
18496     },
18497     onBtnClick : function(id)
18498     {
18499        this.editorcore.relayCmd(id);
18500        this.editorcore.focus();
18501     },
18502     
18503     /**
18504      * Protected method that will not generally be called directly. It triggers
18505      * a toolbar update by reading the markup state of the current selection in the editor.
18506      */
18507     updateToolbar: function(){
18508
18509         if(!this.editorcore.activated){
18510             this.editor.onFirstFocus(); // is this neeed?
18511             return;
18512         }
18513
18514         var btns = this.buttons; 
18515         var doc = this.editorcore.doc;
18516         btns.get('bold').setActive(doc.queryCommandState('bold'));
18517         btns.get('italic').setActive(doc.queryCommandState('italic'));
18518         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18519         
18520         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18521         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18522         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18523         
18524         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18525         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18526          /*
18527         
18528         var ans = this.editorcore.getAllAncestors();
18529         if (this.formatCombo) {
18530             
18531             
18532             var store = this.formatCombo.store;
18533             this.formatCombo.setValue("");
18534             for (var i =0; i < ans.length;i++) {
18535                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18536                     // select it..
18537                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18538                     break;
18539                 }
18540             }
18541         }
18542         
18543         
18544         
18545         // hides menus... - so this cant be on a menu...
18546         Roo.bootstrap.MenuMgr.hideAll();
18547         */
18548         Roo.bootstrap.MenuMgr.hideAll();
18549         //this.editorsyncValue();
18550     },
18551     onFirstFocus: function() {
18552         this.buttons.each(function(item){
18553            item.enable();
18554         });
18555     },
18556     toggleSourceEdit : function(sourceEditMode){
18557         
18558           
18559         if(sourceEditMode){
18560             Roo.log("disabling buttons");
18561            this.buttons.each( function(item){
18562                 if(item.cmd != 'pencil'){
18563                     item.disable();
18564                 }
18565             });
18566           
18567         }else{
18568             Roo.log("enabling buttons");
18569             if(this.editorcore.initialized){
18570                 this.buttons.each( function(item){
18571                     item.enable();
18572                 });
18573             }
18574             
18575         }
18576         Roo.log("calling toggole on editor");
18577         // tell the editor that it's been pressed..
18578         this.editor.toggleSourceEdit(sourceEditMode);
18579        
18580     }
18581 });
18582
18583
18584
18585
18586
18587 /**
18588  * @class Roo.bootstrap.Table.AbstractSelectionModel
18589  * @extends Roo.util.Observable
18590  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18591  * implemented by descendant classes.  This class should not be directly instantiated.
18592  * @constructor
18593  */
18594 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18595     this.locked = false;
18596     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18597 };
18598
18599
18600 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18601     /** @ignore Called by the grid automatically. Do not call directly. */
18602     init : function(grid){
18603         this.grid = grid;
18604         this.initEvents();
18605     },
18606
18607     /**
18608      * Locks the selections.
18609      */
18610     lock : function(){
18611         this.locked = true;
18612     },
18613
18614     /**
18615      * Unlocks the selections.
18616      */
18617     unlock : function(){
18618         this.locked = false;
18619     },
18620
18621     /**
18622      * Returns true if the selections are locked.
18623      * @return {Boolean}
18624      */
18625     isLocked : function(){
18626         return this.locked;
18627     }
18628 });
18629 /**
18630  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18631  * @class Roo.bootstrap.Table.RowSelectionModel
18632  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18633  * It supports multiple selections and keyboard selection/navigation. 
18634  * @constructor
18635  * @param {Object} config
18636  */
18637
18638 Roo.bootstrap.Table.RowSelectionModel = function(config){
18639     Roo.apply(this, config);
18640     this.selections = new Roo.util.MixedCollection(false, function(o){
18641         return o.id;
18642     });
18643
18644     this.last = false;
18645     this.lastActive = false;
18646
18647     this.addEvents({
18648         /**
18649              * @event selectionchange
18650              * Fires when the selection changes
18651              * @param {SelectionModel} this
18652              */
18653             "selectionchange" : true,
18654         /**
18655              * @event afterselectionchange
18656              * Fires after the selection changes (eg. by key press or clicking)
18657              * @param {SelectionModel} this
18658              */
18659             "afterselectionchange" : true,
18660         /**
18661              * @event beforerowselect
18662              * Fires when a row is selected being selected, return false to cancel.
18663              * @param {SelectionModel} this
18664              * @param {Number} rowIndex The selected index
18665              * @param {Boolean} keepExisting False if other selections will be cleared
18666              */
18667             "beforerowselect" : true,
18668         /**
18669              * @event rowselect
18670              * Fires when a row is selected.
18671              * @param {SelectionModel} this
18672              * @param {Number} rowIndex The selected index
18673              * @param {Roo.data.Record} r The record
18674              */
18675             "rowselect" : true,
18676         /**
18677              * @event rowdeselect
18678              * Fires when a row is deselected.
18679              * @param {SelectionModel} this
18680              * @param {Number} rowIndex The selected index
18681              */
18682         "rowdeselect" : true
18683     });
18684     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18685     this.locked = false;
18686 };
18687
18688 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18689     /**
18690      * @cfg {Boolean} singleSelect
18691      * True to allow selection of only one row at a time (defaults to false)
18692      */
18693     singleSelect : false,
18694
18695     // private
18696     initEvents : function(){
18697
18698         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18699             this.grid.on("mousedown", this.handleMouseDown, this);
18700         }else{ // allow click to work like normal
18701             this.grid.on("rowclick", this.handleDragableRowClick, this);
18702         }
18703
18704         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18705             "up" : function(e){
18706                 if(!e.shiftKey){
18707                     this.selectPrevious(e.shiftKey);
18708                 }else if(this.last !== false && this.lastActive !== false){
18709                     var last = this.last;
18710                     this.selectRange(this.last,  this.lastActive-1);
18711                     this.grid.getView().focusRow(this.lastActive);
18712                     if(last !== false){
18713                         this.last = last;
18714                     }
18715                 }else{
18716                     this.selectFirstRow();
18717                 }
18718                 this.fireEvent("afterselectionchange", this);
18719             },
18720             "down" : function(e){
18721                 if(!e.shiftKey){
18722                     this.selectNext(e.shiftKey);
18723                 }else if(this.last !== false && this.lastActive !== false){
18724                     var last = this.last;
18725                     this.selectRange(this.last,  this.lastActive+1);
18726                     this.grid.getView().focusRow(this.lastActive);
18727                     if(last !== false){
18728                         this.last = last;
18729                     }
18730                 }else{
18731                     this.selectFirstRow();
18732                 }
18733                 this.fireEvent("afterselectionchange", this);
18734             },
18735             scope: this
18736         });
18737
18738         var view = this.grid.view;
18739         view.on("refresh", this.onRefresh, this);
18740         view.on("rowupdated", this.onRowUpdated, this);
18741         view.on("rowremoved", this.onRemove, this);
18742     },
18743
18744     // private
18745     onRefresh : function(){
18746         var ds = this.grid.dataSource, i, v = this.grid.view;
18747         var s = this.selections;
18748         s.each(function(r){
18749             if((i = ds.indexOfId(r.id)) != -1){
18750                 v.onRowSelect(i);
18751             }else{
18752                 s.remove(r);
18753             }
18754         });
18755     },
18756
18757     // private
18758     onRemove : function(v, index, r){
18759         this.selections.remove(r);
18760     },
18761
18762     // private
18763     onRowUpdated : function(v, index, r){
18764         if(this.isSelected(r)){
18765             v.onRowSelect(index);
18766         }
18767     },
18768
18769     /**
18770      * Select records.
18771      * @param {Array} records The records to select
18772      * @param {Boolean} keepExisting (optional) True to keep existing selections
18773      */
18774     selectRecords : function(records, keepExisting){
18775         if(!keepExisting){
18776             this.clearSelections();
18777         }
18778         var ds = this.grid.dataSource;
18779         for(var i = 0, len = records.length; i < len; i++){
18780             this.selectRow(ds.indexOf(records[i]), true);
18781         }
18782     },
18783
18784     /**
18785      * Gets the number of selected rows.
18786      * @return {Number}
18787      */
18788     getCount : function(){
18789         return this.selections.length;
18790     },
18791
18792     /**
18793      * Selects the first row in the grid.
18794      */
18795     selectFirstRow : function(){
18796         this.selectRow(0);
18797     },
18798
18799     /**
18800      * Select the last row.
18801      * @param {Boolean} keepExisting (optional) True to keep existing selections
18802      */
18803     selectLastRow : function(keepExisting){
18804         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18805     },
18806
18807     /**
18808      * Selects the row immediately following the last selected row.
18809      * @param {Boolean} keepExisting (optional) True to keep existing selections
18810      */
18811     selectNext : function(keepExisting){
18812         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18813             this.selectRow(this.last+1, keepExisting);
18814             this.grid.getView().focusRow(this.last);
18815         }
18816     },
18817
18818     /**
18819      * Selects the row that precedes the last selected row.
18820      * @param {Boolean} keepExisting (optional) True to keep existing selections
18821      */
18822     selectPrevious : function(keepExisting){
18823         if(this.last){
18824             this.selectRow(this.last-1, keepExisting);
18825             this.grid.getView().focusRow(this.last);
18826         }
18827     },
18828
18829     /**
18830      * Returns the selected records
18831      * @return {Array} Array of selected records
18832      */
18833     getSelections : function(){
18834         return [].concat(this.selections.items);
18835     },
18836
18837     /**
18838      * Returns the first selected record.
18839      * @return {Record}
18840      */
18841     getSelected : function(){
18842         return this.selections.itemAt(0);
18843     },
18844
18845
18846     /**
18847      * Clears all selections.
18848      */
18849     clearSelections : function(fast){
18850         if(this.locked) return;
18851         if(fast !== true){
18852             var ds = this.grid.dataSource;
18853             var s = this.selections;
18854             s.each(function(r){
18855                 this.deselectRow(ds.indexOfId(r.id));
18856             }, this);
18857             s.clear();
18858         }else{
18859             this.selections.clear();
18860         }
18861         this.last = false;
18862     },
18863
18864
18865     /**
18866      * Selects all rows.
18867      */
18868     selectAll : function(){
18869         if(this.locked) return;
18870         this.selections.clear();
18871         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18872             this.selectRow(i, true);
18873         }
18874     },
18875
18876     /**
18877      * Returns True if there is a selection.
18878      * @return {Boolean}
18879      */
18880     hasSelection : function(){
18881         return this.selections.length > 0;
18882     },
18883
18884     /**
18885      * Returns True if the specified row is selected.
18886      * @param {Number/Record} record The record or index of the record to check
18887      * @return {Boolean}
18888      */
18889     isSelected : function(index){
18890         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18891         return (r && this.selections.key(r.id) ? true : false);
18892     },
18893
18894     /**
18895      * Returns True if the specified record id is selected.
18896      * @param {String} id The id of record to check
18897      * @return {Boolean}
18898      */
18899     isIdSelected : function(id){
18900         return (this.selections.key(id) ? true : false);
18901     },
18902
18903     // private
18904     handleMouseDown : function(e, t){
18905         var view = this.grid.getView(), rowIndex;
18906         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18907             return;
18908         };
18909         if(e.shiftKey && this.last !== false){
18910             var last = this.last;
18911             this.selectRange(last, rowIndex, e.ctrlKey);
18912             this.last = last; // reset the last
18913             view.focusRow(rowIndex);
18914         }else{
18915             var isSelected = this.isSelected(rowIndex);
18916             if(e.button !== 0 && isSelected){
18917                 view.focusRow(rowIndex);
18918             }else if(e.ctrlKey && isSelected){
18919                 this.deselectRow(rowIndex);
18920             }else if(!isSelected){
18921                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18922                 view.focusRow(rowIndex);
18923             }
18924         }
18925         this.fireEvent("afterselectionchange", this);
18926     },
18927     // private
18928     handleDragableRowClick :  function(grid, rowIndex, e) 
18929     {
18930         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18931             this.selectRow(rowIndex, false);
18932             grid.view.focusRow(rowIndex);
18933              this.fireEvent("afterselectionchange", this);
18934         }
18935     },
18936     
18937     /**
18938      * Selects multiple rows.
18939      * @param {Array} rows Array of the indexes of the row to select
18940      * @param {Boolean} keepExisting (optional) True to keep existing selections
18941      */
18942     selectRows : function(rows, keepExisting){
18943         if(!keepExisting){
18944             this.clearSelections();
18945         }
18946         for(var i = 0, len = rows.length; i < len; i++){
18947             this.selectRow(rows[i], true);
18948         }
18949     },
18950
18951     /**
18952      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18953      * @param {Number} startRow The index of the first row in the range
18954      * @param {Number} endRow The index of the last row in the range
18955      * @param {Boolean} keepExisting (optional) True to retain existing selections
18956      */
18957     selectRange : function(startRow, endRow, keepExisting){
18958         if(this.locked) return;
18959         if(!keepExisting){
18960             this.clearSelections();
18961         }
18962         if(startRow <= endRow){
18963             for(var i = startRow; i <= endRow; i++){
18964                 this.selectRow(i, true);
18965             }
18966         }else{
18967             for(var i = startRow; i >= endRow; i--){
18968                 this.selectRow(i, true);
18969             }
18970         }
18971     },
18972
18973     /**
18974      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18975      * @param {Number} startRow The index of the first row in the range
18976      * @param {Number} endRow The index of the last row in the range
18977      */
18978     deselectRange : function(startRow, endRow, preventViewNotify){
18979         if(this.locked) return;
18980         for(var i = startRow; i <= endRow; i++){
18981             this.deselectRow(i, preventViewNotify);
18982         }
18983     },
18984
18985     /**
18986      * Selects a row.
18987      * @param {Number} row The index of the row to select
18988      * @param {Boolean} keepExisting (optional) True to keep existing selections
18989      */
18990     selectRow : function(index, keepExisting, preventViewNotify){
18991         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18992         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18993             if(!keepExisting || this.singleSelect){
18994                 this.clearSelections();
18995             }
18996             var r = this.grid.dataSource.getAt(index);
18997             this.selections.add(r);
18998             this.last = this.lastActive = index;
18999             if(!preventViewNotify){
19000                 this.grid.getView().onRowSelect(index);
19001             }
19002             this.fireEvent("rowselect", this, index, r);
19003             this.fireEvent("selectionchange", this);
19004         }
19005     },
19006
19007     /**
19008      * Deselects a row.
19009      * @param {Number} row The index of the row to deselect
19010      */
19011     deselectRow : function(index, preventViewNotify){
19012         if(this.locked) return;
19013         if(this.last == index){
19014             this.last = false;
19015         }
19016         if(this.lastActive == index){
19017             this.lastActive = false;
19018         }
19019         var r = this.grid.dataSource.getAt(index);
19020         this.selections.remove(r);
19021         if(!preventViewNotify){
19022             this.grid.getView().onRowDeselect(index);
19023         }
19024         this.fireEvent("rowdeselect", this, index);
19025         this.fireEvent("selectionchange", this);
19026     },
19027
19028     // private
19029     restoreLast : function(){
19030         if(this._last){
19031             this.last = this._last;
19032         }
19033     },
19034
19035     // private
19036     acceptsNav : function(row, col, cm){
19037         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19038     },
19039
19040     // private
19041     onEditorKey : function(field, e){
19042         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19043         if(k == e.TAB){
19044             e.stopEvent();
19045             ed.completeEdit();
19046             if(e.shiftKey){
19047                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19048             }else{
19049                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19050             }
19051         }else if(k == e.ENTER && !e.ctrlKey){
19052             e.stopEvent();
19053             ed.completeEdit();
19054             if(e.shiftKey){
19055                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19056             }else{
19057                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19058             }
19059         }else if(k == e.ESC){
19060             ed.cancelEdit();
19061         }
19062         if(newCell){
19063             g.startEditing(newCell[0], newCell[1]);
19064         }
19065     }
19066 });/*
19067  * Based on:
19068  * Ext JS Library 1.1.1
19069  * Copyright(c) 2006-2007, Ext JS, LLC.
19070  *
19071  * Originally Released Under LGPL - original licence link has changed is not relivant.
19072  *
19073  * Fork - LGPL
19074  * <script type="text/javascript">
19075  */
19076  
19077 /**
19078  * @class Roo.bootstrap.PagingToolbar
19079  * @extends Roo.Row
19080  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19081  * @constructor
19082  * Create a new PagingToolbar
19083  * @param {Object} config The config object
19084  */
19085 Roo.bootstrap.PagingToolbar = function(config)
19086 {
19087     // old args format still supported... - xtype is prefered..
19088         // created from xtype...
19089     var ds = config.dataSource;
19090     this.toolbarItems = [];
19091     if (config.items) {
19092         this.toolbarItems = config.items;
19093 //        config.items = [];
19094     }
19095     
19096     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19097     this.ds = ds;
19098     this.cursor = 0;
19099     if (ds) { 
19100         this.bind(ds);
19101     }
19102     
19103     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19104     
19105 };
19106
19107 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19108     /**
19109      * @cfg {Roo.data.Store} dataSource
19110      * The underlying data store providing the paged data
19111      */
19112     /**
19113      * @cfg {String/HTMLElement/Element} container
19114      * container The id or element that will contain the toolbar
19115      */
19116     /**
19117      * @cfg {Boolean} displayInfo
19118      * True to display the displayMsg (defaults to false)
19119      */
19120     /**
19121      * @cfg {Number} pageSize
19122      * The number of records to display per page (defaults to 20)
19123      */
19124     pageSize: 20,
19125     /**
19126      * @cfg {String} displayMsg
19127      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19128      */
19129     displayMsg : 'Displaying {0} - {1} of {2}',
19130     /**
19131      * @cfg {String} emptyMsg
19132      * The message to display when no records are found (defaults to "No data to display")
19133      */
19134     emptyMsg : 'No data to display',
19135     /**
19136      * Customizable piece of the default paging text (defaults to "Page")
19137      * @type String
19138      */
19139     beforePageText : "Page",
19140     /**
19141      * Customizable piece of the default paging text (defaults to "of %0")
19142      * @type String
19143      */
19144     afterPageText : "of {0}",
19145     /**
19146      * Customizable piece of the default paging text (defaults to "First Page")
19147      * @type String
19148      */
19149     firstText : "First Page",
19150     /**
19151      * Customizable piece of the default paging text (defaults to "Previous Page")
19152      * @type String
19153      */
19154     prevText : "Previous Page",
19155     /**
19156      * Customizable piece of the default paging text (defaults to "Next Page")
19157      * @type String
19158      */
19159     nextText : "Next Page",
19160     /**
19161      * Customizable piece of the default paging text (defaults to "Last Page")
19162      * @type String
19163      */
19164     lastText : "Last Page",
19165     /**
19166      * Customizable piece of the default paging text (defaults to "Refresh")
19167      * @type String
19168      */
19169     refreshText : "Refresh",
19170
19171     buttons : false,
19172     // private
19173     onRender : function(ct, position) 
19174     {
19175         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19176         this.navgroup.parentId = this.id;
19177         this.navgroup.onRender(this.el, null);
19178         // add the buttons to the navgroup
19179         
19180         if(this.displayInfo){
19181             Roo.log(this.el.select('ul.navbar-nav',true).first());
19182             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19183             this.displayEl = this.el.select('.x-paging-info', true).first();
19184 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19185 //            this.displayEl = navel.el.select('span',true).first();
19186         }
19187         
19188         var _this = this;
19189         
19190         if(this.buttons){
19191             Roo.each(_this.buttons, function(e){
19192                Roo.factory(e).onRender(_this.el, null);
19193             });
19194         }
19195             
19196         Roo.each(_this.toolbarItems, function(e) {
19197             _this.navgroup.addItem(e);
19198         });
19199         
19200         this.first = this.navgroup.addItem({
19201             tooltip: this.firstText,
19202             cls: "prev",
19203             icon : 'fa fa-backward',
19204             disabled: true,
19205             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19206         });
19207         
19208         this.prev =  this.navgroup.addItem({
19209             tooltip: this.prevText,
19210             cls: "prev",
19211             icon : 'fa fa-step-backward',
19212             disabled: true,
19213             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19214         });
19215     //this.addSeparator();
19216         
19217         
19218         var field = this.navgroup.addItem( {
19219             tagtype : 'span',
19220             cls : 'x-paging-position',
19221             
19222             html : this.beforePageText  +
19223                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19224                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19225          } ); //?? escaped?
19226         
19227         this.field = field.el.select('input', true).first();
19228         this.field.on("keydown", this.onPagingKeydown, this);
19229         this.field.on("focus", function(){this.dom.select();});
19230     
19231     
19232         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19233         //this.field.setHeight(18);
19234         //this.addSeparator();
19235         this.next = this.navgroup.addItem({
19236             tooltip: this.nextText,
19237             cls: "next",
19238             html : ' <i class="fa fa-step-forward">',
19239             disabled: true,
19240             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19241         });
19242         this.last = this.navgroup.addItem({
19243             tooltip: this.lastText,
19244             icon : 'fa fa-forward',
19245             cls: "next",
19246             disabled: true,
19247             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19248         });
19249     //this.addSeparator();
19250         this.loading = this.navgroup.addItem({
19251             tooltip: this.refreshText,
19252             icon: 'fa fa-refresh',
19253             
19254             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19255         });
19256
19257     },
19258
19259     // private
19260     updateInfo : function(){
19261         if(this.displayEl){
19262             var count = this.ds.getCount();
19263             var msg = count == 0 ?
19264                 this.emptyMsg :
19265                 String.format(
19266                     this.displayMsg,
19267                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19268                 );
19269             this.displayEl.update(msg);
19270         }
19271     },
19272
19273     // private
19274     onLoad : function(ds, r, o){
19275        this.cursor = o.params ? o.params.start : 0;
19276        var d = this.getPageData(),
19277             ap = d.activePage,
19278             ps = d.pages;
19279         
19280        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19281        this.field.dom.value = ap;
19282        this.first.setDisabled(ap == 1);
19283        this.prev.setDisabled(ap == 1);
19284        this.next.setDisabled(ap == ps);
19285        this.last.setDisabled(ap == ps);
19286        this.loading.enable();
19287        this.updateInfo();
19288     },
19289
19290     // private
19291     getPageData : function(){
19292         var total = this.ds.getTotalCount();
19293         return {
19294             total : total,
19295             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19296             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19297         };
19298     },
19299
19300     // private
19301     onLoadError : function(){
19302         this.loading.enable();
19303     },
19304
19305     // private
19306     onPagingKeydown : function(e){
19307         var k = e.getKey();
19308         var d = this.getPageData();
19309         if(k == e.RETURN){
19310             var v = this.field.dom.value, pageNum;
19311             if(!v || isNaN(pageNum = parseInt(v, 10))){
19312                 this.field.dom.value = d.activePage;
19313                 return;
19314             }
19315             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19316             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19317             e.stopEvent();
19318         }
19319         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))
19320         {
19321           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19322           this.field.dom.value = pageNum;
19323           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19324           e.stopEvent();
19325         }
19326         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19327         {
19328           var v = this.field.dom.value, pageNum; 
19329           var increment = (e.shiftKey) ? 10 : 1;
19330           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19331             increment *= -1;
19332           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19333             this.field.dom.value = d.activePage;
19334             return;
19335           }
19336           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19337           {
19338             this.field.dom.value = parseInt(v, 10) + increment;
19339             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19340             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19341           }
19342           e.stopEvent();
19343         }
19344     },
19345
19346     // private
19347     beforeLoad : function(){
19348         if(this.loading){
19349             this.loading.disable();
19350         }
19351     },
19352
19353     // private
19354     onClick : function(which){
19355         var ds = this.ds;
19356         if (!ds) {
19357             return;
19358         }
19359         switch(which){
19360             case "first":
19361                 ds.load({params:{start: 0, limit: this.pageSize}});
19362             break;
19363             case "prev":
19364                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19365             break;
19366             case "next":
19367                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19368             break;
19369             case "last":
19370                 var total = ds.getTotalCount();
19371                 var extra = total % this.pageSize;
19372                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19373                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19374             break;
19375             case "refresh":
19376                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19377             break;
19378         }
19379     },
19380
19381     /**
19382      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19383      * @param {Roo.data.Store} store The data store to unbind
19384      */
19385     unbind : function(ds){
19386         ds.un("beforeload", this.beforeLoad, this);
19387         ds.un("load", this.onLoad, this);
19388         ds.un("loadexception", this.onLoadError, this);
19389         ds.un("remove", this.updateInfo, this);
19390         ds.un("add", this.updateInfo, this);
19391         this.ds = undefined;
19392     },
19393
19394     /**
19395      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19396      * @param {Roo.data.Store} store The data store to bind
19397      */
19398     bind : function(ds){
19399         ds.on("beforeload", this.beforeLoad, this);
19400         ds.on("load", this.onLoad, this);
19401         ds.on("loadexception", this.onLoadError, this);
19402         ds.on("remove", this.updateInfo, this);
19403         ds.on("add", this.updateInfo, this);
19404         this.ds = ds;
19405     }
19406 });/*
19407  * - LGPL
19408  *
19409  * element
19410  * 
19411  */
19412
19413 /**
19414  * @class Roo.bootstrap.MessageBar
19415  * @extends Roo.bootstrap.Component
19416  * Bootstrap MessageBar class
19417  * @cfg {String} html contents of the MessageBar
19418  * @cfg {String} weight (info | success | warning | danger) default info
19419  * @cfg {String} beforeClass insert the bar before the given class
19420  * @cfg {Boolean} closable (true | false) default false
19421  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19422  * 
19423  * @constructor
19424  * Create a new Element
19425  * @param {Object} config The config object
19426  */
19427
19428 Roo.bootstrap.MessageBar = function(config){
19429     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19430 };
19431
19432 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19433     
19434     html: '',
19435     weight: 'info',
19436     closable: false,
19437     fixed: false,
19438     beforeClass: 'bootstrap-sticky-wrap',
19439     
19440     getAutoCreate : function(){
19441         
19442         var cfg = {
19443             tag: 'div',
19444             cls: 'alert alert-dismissable alert-' + this.weight,
19445             cn: [
19446                 {
19447                     tag: 'span',
19448                     cls: 'message',
19449                     html: this.html || ''
19450                 }
19451             ]
19452         }
19453         
19454         if(this.fixed){
19455             cfg.cls += ' alert-messages-fixed';
19456         }
19457         
19458         if(this.closable){
19459             cfg.cn.push({
19460                 tag: 'button',
19461                 cls: 'close',
19462                 html: 'x'
19463             });
19464         }
19465         
19466         return cfg;
19467     },
19468     
19469     onRender : function(ct, position)
19470     {
19471         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19472         
19473         if(!this.el){
19474             var cfg = Roo.apply({},  this.getAutoCreate());
19475             cfg.id = Roo.id();
19476             
19477             if (this.cls) {
19478                 cfg.cls += ' ' + this.cls;
19479             }
19480             if (this.style) {
19481                 cfg.style = this.style;
19482             }
19483             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19484             
19485             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19486         }
19487         
19488         this.el.select('>button.close').on('click', this.hide, this);
19489         
19490     },
19491     
19492     show : function()
19493     {
19494         if (!this.rendered) {
19495             this.render();
19496         }
19497         
19498         this.el.show();
19499         
19500         this.fireEvent('show', this);
19501         
19502     },
19503     
19504     hide : function()
19505     {
19506         if (!this.rendered) {
19507             this.render();
19508         }
19509         
19510         this.el.hide();
19511         
19512         this.fireEvent('hide', this);
19513     },
19514     
19515     update : function()
19516     {
19517 //        var e = this.el.dom.firstChild;
19518 //        
19519 //        if(this.closable){
19520 //            e = e.nextSibling;
19521 //        }
19522 //        
19523 //        e.data = this.html || '';
19524
19525         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19526     }
19527    
19528 });
19529
19530  
19531
19532      /*
19533  * - LGPL
19534  *
19535  * Graph
19536  * 
19537  */
19538
19539
19540 /**
19541  * @class Roo.bootstrap.Graph
19542  * @extends Roo.bootstrap.Component
19543  * Bootstrap Graph class
19544 > Prameters
19545  -sm {number} sm 4
19546  -md {number} md 5
19547  @cfg {String} graphtype  bar | vbar | pie
19548  @cfg {number} g_x coodinator | centre x (pie)
19549  @cfg {number} g_y coodinator | centre y (pie)
19550  @cfg {number} g_r radius (pie)
19551  @cfg {number} g_height height of the chart (respected by all elements in the set)
19552  @cfg {number} g_width width of the chart (respected by all elements in the set)
19553  @cfg {Object} title The title of the chart
19554     
19555  -{Array}  values
19556  -opts (object) options for the chart 
19557      o {
19558      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19559      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19560      o vgutter (number)
19561      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.
19562      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19563      o to
19564      o stretch (boolean)
19565      o }
19566  -opts (object) options for the pie
19567      o{
19568      o cut
19569      o startAngle (number)
19570      o endAngle (number)
19571      } 
19572  *
19573  * @constructor
19574  * Create a new Input
19575  * @param {Object} config The config object
19576  */
19577
19578 Roo.bootstrap.Graph = function(config){
19579     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19580     
19581     this.addEvents({
19582         // img events
19583         /**
19584          * @event click
19585          * The img click event for the img.
19586          * @param {Roo.EventObject} e
19587          */
19588         "click" : true
19589     });
19590 };
19591
19592 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19593     
19594     sm: 4,
19595     md: 5,
19596     graphtype: 'bar',
19597     g_height: 250,
19598     g_width: 400,
19599     g_x: 50,
19600     g_y: 50,
19601     g_r: 30,
19602     opts:{
19603         //g_colors: this.colors,
19604         g_type: 'soft',
19605         g_gutter: '20%'
19606
19607     },
19608     title : false,
19609
19610     getAutoCreate : function(){
19611         
19612         var cfg = {
19613             tag: 'div',
19614             html : null
19615         }
19616         
19617         
19618         return  cfg;
19619     },
19620
19621     onRender : function(ct,position){
19622         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19623         this.raphael = Raphael(this.el.dom);
19624         
19625                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19626                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19627                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19628                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19629                 /*
19630                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19631                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19632                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19633                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19634                 
19635                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19636                 r.barchart(330, 10, 300, 220, data1);
19637                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19638                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19639                 */
19640                 
19641                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19642                 // r.barchart(30, 30, 560, 250,  xdata, {
19643                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19644                 //     axis : "0 0 1 1",
19645                 //     axisxlabels :  xdata
19646                 //     //yvalues : cols,
19647                    
19648                 // });
19649 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19650 //        
19651 //        this.load(null,xdata,{
19652 //                axis : "0 0 1 1",
19653 //                axisxlabels :  xdata
19654 //                });
19655
19656     },
19657
19658     load : function(graphtype,xdata,opts){
19659         this.raphael.clear();
19660         if(!graphtype) {
19661             graphtype = this.graphtype;
19662         }
19663         if(!opts){
19664             opts = this.opts;
19665         }
19666         var r = this.raphael,
19667             fin = function () {
19668                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19669             },
19670             fout = function () {
19671                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19672             },
19673             pfin = function() {
19674                 this.sector.stop();
19675                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19676
19677                 if (this.label) {
19678                     this.label[0].stop();
19679                     this.label[0].attr({ r: 7.5 });
19680                     this.label[1].attr({ "font-weight": 800 });
19681                 }
19682             },
19683             pfout = function() {
19684                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19685
19686                 if (this.label) {
19687                     this.label[0].animate({ r: 5 }, 500, "bounce");
19688                     this.label[1].attr({ "font-weight": 400 });
19689                 }
19690             };
19691
19692         switch(graphtype){
19693             case 'bar':
19694                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19695                 break;
19696             case 'hbar':
19697                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19698                 break;
19699             case 'pie':
19700 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19701 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19702 //            
19703                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19704                 
19705                 break;
19706
19707         }
19708         
19709         if(this.title){
19710             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19711         }
19712         
19713     },
19714     
19715     setTitle: function(o)
19716     {
19717         this.title = o;
19718     },
19719     
19720     initEvents: function() {
19721         
19722         if(!this.href){
19723             this.el.on('click', this.onClick, this);
19724         }
19725     },
19726     
19727     onClick : function(e)
19728     {
19729         Roo.log('img onclick');
19730         this.fireEvent('click', this, e);
19731     }
19732    
19733 });
19734
19735  
19736 /*
19737  * - LGPL
19738  *
19739  * numberBox
19740  * 
19741  */
19742 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19743
19744 /**
19745  * @class Roo.bootstrap.dash.NumberBox
19746  * @extends Roo.bootstrap.Component
19747  * Bootstrap NumberBox class
19748  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19749  * @cfg {String} headline Box headline
19750  * @cfg {String} content Box content
19751  * @cfg {String} icon Box icon
19752  * @cfg {String} footer Footer text
19753  * @cfg {String} fhref Footer href
19754  * 
19755  * @constructor
19756  * Create a new NumberBox
19757  * @param {Object} config The config object
19758  */
19759
19760
19761 Roo.bootstrap.dash.NumberBox = function(config){
19762     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19763     
19764 };
19765
19766 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19767     
19768     bgcolor : 'aqua',
19769     headline : '',
19770     content : '',
19771     icon : '',
19772     footer : '',
19773     fhref : '',
19774     ficon : '',
19775     
19776     getAutoCreate : function(){
19777         
19778         var cfg = {
19779             tag : 'div',
19780             cls : 'small-box bg-' + this.bgcolor,
19781             cn : [
19782                 {
19783                     tag : 'div',
19784                     cls : 'inner',
19785                     cn :[
19786                         {
19787                             tag : 'h3',
19788                             cls : 'roo-headline',
19789                             html : this.headline
19790                         },
19791                         {
19792                             tag : 'p',
19793                             cls : 'roo-content',
19794                             html : this.content
19795                         }
19796                     ]
19797                 }
19798             ]
19799         }
19800         
19801         if(this.icon){
19802             cfg.cn.push({
19803                 tag : 'div',
19804                 cls : 'icon',
19805                 cn :[
19806                     {
19807                         tag : 'i',
19808                         cls : 'ion ' + this.icon
19809                     }
19810                 ]
19811             });
19812         }
19813         
19814         if(this.footer){
19815             var footer = {
19816                 tag : 'a',
19817                 cls : 'small-box-footer',
19818                 href : this.fhref || '#',
19819                 html : this.footer
19820             };
19821             
19822             cfg.cn.push(footer);
19823             
19824         }
19825         
19826         return  cfg;
19827     },
19828
19829     onRender : function(ct,position){
19830         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19831
19832
19833        
19834                 
19835     },
19836
19837     setHeadline: function (value)
19838     {
19839         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19840     },
19841     
19842     setFooter: function (value, href)
19843     {
19844         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19845         
19846         if(href){
19847             this.el.select('a.small-box-footer',true).first().attr('href', href);
19848         }
19849         
19850     },
19851
19852     setContent: function (value)
19853     {
19854         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19855     },
19856
19857     initEvents: function() 
19858     {   
19859         
19860     }
19861     
19862 });
19863
19864  
19865 /*
19866  * - LGPL
19867  *
19868  * TabBox
19869  * 
19870  */
19871 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19872
19873 /**
19874  * @class Roo.bootstrap.dash.TabBox
19875  * @extends Roo.bootstrap.Component
19876  * Bootstrap TabBox class
19877  * @cfg {String} title Title of the TabBox
19878  * @cfg {String} icon Icon of the TabBox
19879  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19880  * 
19881  * @constructor
19882  * Create a new TabBox
19883  * @param {Object} config The config object
19884  */
19885
19886
19887 Roo.bootstrap.dash.TabBox = function(config){
19888     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19889     this.addEvents({
19890         // raw events
19891         /**
19892          * @event addpane
19893          * When a pane is added
19894          * @param {Roo.bootstrap.dash.TabPane} pane
19895          */
19896         "addpane" : true
19897          
19898     });
19899 };
19900
19901 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19902
19903     title : '',
19904     icon : false,
19905     showtabs : true,
19906     
19907     getChildContainer : function()
19908     {
19909         return this.el.select('.tab-content', true).first();
19910     },
19911     
19912     getAutoCreate : function(){
19913         
19914         var header = {
19915             tag: 'li',
19916             cls: 'pull-left header',
19917             html: this.title,
19918             cn : []
19919         };
19920         
19921         if(this.icon){
19922             header.cn.push({
19923                 tag: 'i',
19924                 cls: 'fa ' + this.icon
19925             });
19926         }
19927         
19928         
19929         var cfg = {
19930             tag: 'div',
19931             cls: 'nav-tabs-custom',
19932             cn: [
19933                 {
19934                     tag: 'ul',
19935                     cls: 'nav nav-tabs pull-right',
19936                     cn: [
19937                         header
19938                     ]
19939                 },
19940                 {
19941                     tag: 'div',
19942                     cls: 'tab-content no-padding',
19943                     cn: []
19944                 }
19945             ]
19946         }
19947
19948         return  cfg;
19949     },
19950     initEvents : function()
19951     {
19952         //Roo.log('add add pane handler');
19953         this.on('addpane', this.onAddPane, this);
19954     },
19955      /**
19956      * Updates the box title
19957      * @param {String} html to set the title to.
19958      */
19959     setTitle : function(value)
19960     {
19961         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19962     },
19963     onAddPane : function(pane)
19964     {
19965         //Roo.log('addpane');
19966         //Roo.log(pane);
19967         // tabs are rendere left to right..
19968         if(!this.showtabs){
19969             return;
19970         }
19971         
19972         var ctr = this.el.select('.nav-tabs', true).first();
19973          
19974          
19975         var existing = ctr.select('.nav-tab',true);
19976         var qty = existing.getCount();;
19977         
19978         
19979         var tab = ctr.createChild({
19980             tag : 'li',
19981             cls : 'nav-tab' + (qty ? '' : ' active'),
19982             cn : [
19983                 {
19984                     tag : 'a',
19985                     href:'#',
19986                     html : pane.title
19987                 }
19988             ]
19989         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19990         pane.tab = tab;
19991         
19992         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19993         if (!qty) {
19994             pane.el.addClass('active');
19995         }
19996         
19997                 
19998     },
19999     onTabClick : function(ev,un,ob,pane)
20000     {
20001         //Roo.log('tab - prev default');
20002         ev.preventDefault();
20003         
20004         
20005         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20006         pane.tab.addClass('active');
20007         //Roo.log(pane.title);
20008         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20009         // technically we should have a deactivate event.. but maybe add later.
20010         // and it should not de-activate the selected tab...
20011         
20012         pane.el.addClass('active');
20013         pane.fireEvent('activate');
20014         
20015         
20016     }
20017     
20018     
20019 });
20020
20021  
20022 /*
20023  * - LGPL
20024  *
20025  * Tab pane
20026  * 
20027  */
20028 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20029 /**
20030  * @class Roo.bootstrap.TabPane
20031  * @extends Roo.bootstrap.Component
20032  * Bootstrap TabPane class
20033  * @cfg {Boolean} active (false | true) Default false
20034  * @cfg {String} title title of panel
20035
20036  * 
20037  * @constructor
20038  * Create a new TabPane
20039  * @param {Object} config The config object
20040  */
20041
20042 Roo.bootstrap.dash.TabPane = function(config){
20043     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20044     
20045     this.addEvents({
20046         // raw events
20047         /**
20048          * @event activate
20049          * When a pane is activated
20050          * @param {Roo.bootstrap.dash.TabPane} pane
20051          */
20052         "activate" : true
20053          
20054     });
20055 };
20056
20057 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20058     
20059     active : false,
20060     title : '',
20061     
20062     // the tabBox that this is attached to.
20063     tab : false,
20064      
20065     getAutoCreate : function() 
20066     {
20067         var cfg = {
20068             tag: 'div',
20069             cls: 'tab-pane'
20070         }
20071         
20072         if(this.active){
20073             cfg.cls += ' active';
20074         }
20075         
20076         return cfg;
20077     },
20078     initEvents  : function()
20079     {
20080         //Roo.log('trigger add pane handler');
20081         this.parent().fireEvent('addpane', this)
20082     },
20083     
20084      /**
20085      * Updates the tab title 
20086      * @param {String} html to set the title to.
20087      */
20088     setTitle: function(str)
20089     {
20090         if (!this.tab) {
20091             return;
20092         }
20093         this.title = str;
20094         this.tab.select('a', true).first().dom.innerHTML = str;
20095         
20096     }
20097     
20098     
20099     
20100 });
20101
20102  
20103
20104
20105  /*
20106  * - LGPL
20107  *
20108  * menu
20109  * 
20110  */
20111 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20112
20113 /**
20114  * @class Roo.bootstrap.menu.Menu
20115  * @extends Roo.bootstrap.Component
20116  * Bootstrap Menu class - container for Menu
20117  * @cfg {String} html Text of the menu
20118  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20119  * @cfg {String} icon Font awesome icon
20120  * @cfg {String} pos Menu align to (top | bottom) default bottom
20121  * 
20122  * 
20123  * @constructor
20124  * Create a new Menu
20125  * @param {Object} config The config object
20126  */
20127
20128
20129 Roo.bootstrap.menu.Menu = function(config){
20130     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20131     
20132     this.addEvents({
20133         /**
20134          * @event beforeshow
20135          * Fires before this menu is displayed
20136          * @param {Roo.bootstrap.menu.Menu} this
20137          */
20138         beforeshow : true,
20139         /**
20140          * @event beforehide
20141          * Fires before this menu is hidden
20142          * @param {Roo.bootstrap.menu.Menu} this
20143          */
20144         beforehide : true,
20145         /**
20146          * @event show
20147          * Fires after this menu is displayed
20148          * @param {Roo.bootstrap.menu.Menu} this
20149          */
20150         show : true,
20151         /**
20152          * @event hide
20153          * Fires after this menu is hidden
20154          * @param {Roo.bootstrap.menu.Menu} this
20155          */
20156         hide : true,
20157         /**
20158          * @event click
20159          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20160          * @param {Roo.bootstrap.menu.Menu} this
20161          * @param {Roo.EventObject} e
20162          */
20163         click : true
20164     });
20165     
20166 };
20167
20168 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20169     
20170     submenu : false,
20171     html : '',
20172     weight : 'default',
20173     icon : false,
20174     pos : 'bottom',
20175     
20176     
20177     getChildContainer : function() {
20178         if(this.isSubMenu){
20179             return this.el;
20180         }
20181         
20182         return this.el.select('ul.dropdown-menu', true).first();  
20183     },
20184     
20185     getAutoCreate : function()
20186     {
20187         var text = [
20188             {
20189                 tag : 'span',
20190                 cls : 'roo-menu-text',
20191                 html : this.html
20192             }
20193         ];
20194         
20195         if(this.icon){
20196             text.unshift({
20197                 tag : 'i',
20198                 cls : 'fa ' + this.icon
20199             })
20200         }
20201         
20202         
20203         var cfg = {
20204             tag : 'div',
20205             cls : 'btn-group',
20206             cn : [
20207                 {
20208                     tag : 'button',
20209                     cls : 'dropdown-button btn btn-' + this.weight,
20210                     cn : text
20211                 },
20212                 {
20213                     tag : 'button',
20214                     cls : 'dropdown-toggle btn btn-' + this.weight,
20215                     cn : [
20216                         {
20217                             tag : 'span',
20218                             cls : 'caret'
20219                         }
20220                     ]
20221                 },
20222                 {
20223                     tag : 'ul',
20224                     cls : 'dropdown-menu'
20225                 }
20226             ]
20227             
20228         };
20229         
20230         if(this.pos == 'top'){
20231             cfg.cls += ' dropup';
20232         }
20233         
20234         if(this.isSubMenu){
20235             cfg = {
20236                 tag : 'ul',
20237                 cls : 'dropdown-menu'
20238             }
20239         }
20240         
20241         return cfg;
20242     },
20243     
20244     onRender : function(ct, position)
20245     {
20246         this.isSubMenu = ct.hasClass('dropdown-submenu');
20247         
20248         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20249     },
20250     
20251     initEvents : function() 
20252     {
20253         if(this.isSubMenu){
20254             return;
20255         }
20256         
20257         this.hidden = true;
20258         
20259         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20260         this.triggerEl.on('click', this.onTriggerPress, this);
20261         
20262         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20263         this.buttonEl.on('click', this.onClick, this);
20264         
20265     },
20266     
20267     list : function()
20268     {
20269         if(this.isSubMenu){
20270             return this.el;
20271         }
20272         
20273         return this.el.select('ul.dropdown-menu', true).first();
20274     },
20275     
20276     onClick : function(e)
20277     {
20278         this.fireEvent("click", this, e);
20279     },
20280     
20281     onTriggerPress  : function(e)
20282     {   
20283         if (this.isVisible()) {
20284             this.hide();
20285         } else {
20286             this.show();
20287         }
20288     },
20289     
20290     isVisible : function(){
20291         return !this.hidden;
20292     },
20293     
20294     show : function()
20295     {
20296         this.fireEvent("beforeshow", this);
20297         
20298         this.hidden = false;
20299         this.el.addClass('open');
20300         
20301         Roo.get(document).on("mouseup", this.onMouseUp, this);
20302         
20303         this.fireEvent("show", this);
20304         
20305         
20306     },
20307     
20308     hide : function()
20309     {
20310         this.fireEvent("beforehide", this);
20311         
20312         this.hidden = true;
20313         this.el.removeClass('open');
20314         
20315         Roo.get(document).un("mouseup", this.onMouseUp);
20316         
20317         this.fireEvent("hide", this);
20318     },
20319     
20320     onMouseUp : function()
20321     {
20322         this.hide();
20323     }
20324     
20325 });
20326
20327  
20328  /*
20329  * - LGPL
20330  *
20331  * menu item
20332  * 
20333  */
20334 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20335
20336 /**
20337  * @class Roo.bootstrap.menu.Item
20338  * @extends Roo.bootstrap.Component
20339  * Bootstrap MenuItem class
20340  * @cfg {Boolean} submenu (true | false) default false
20341  * @cfg {String} html text of the item
20342  * @cfg {String} href the link
20343  * @cfg {Boolean} disable (true | false) default false
20344  * @cfg {Boolean} preventDefault (true | false) default true
20345  * @cfg {String} icon Font awesome icon
20346  * @cfg {String} pos Submenu align to (left | right) default right 
20347  * 
20348  * 
20349  * @constructor
20350  * Create a new Item
20351  * @param {Object} config The config object
20352  */
20353
20354
20355 Roo.bootstrap.menu.Item = function(config){
20356     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20357     this.addEvents({
20358         /**
20359          * @event mouseover
20360          * Fires when the mouse is hovering over this menu
20361          * @param {Roo.bootstrap.menu.Item} this
20362          * @param {Roo.EventObject} e
20363          */
20364         mouseover : true,
20365         /**
20366          * @event mouseout
20367          * Fires when the mouse exits this menu
20368          * @param {Roo.bootstrap.menu.Item} this
20369          * @param {Roo.EventObject} e
20370          */
20371         mouseout : true,
20372         // raw events
20373         /**
20374          * @event click
20375          * The raw click event for the entire grid.
20376          * @param {Roo.EventObject} e
20377          */
20378         click : true
20379     });
20380 };
20381
20382 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20383     
20384     submenu : false,
20385     href : '',
20386     html : '',
20387     preventDefault: true,
20388     disable : false,
20389     icon : false,
20390     pos : 'right',
20391     
20392     getAutoCreate : function()
20393     {
20394         var text = [
20395             {
20396                 tag : 'span',
20397                 cls : 'roo-menu-item-text',
20398                 html : this.html
20399             }
20400         ];
20401         
20402         if(this.icon){
20403             text.unshift({
20404                 tag : 'i',
20405                 cls : 'fa ' + this.icon
20406             })
20407         }
20408         
20409         var cfg = {
20410             tag : 'li',
20411             cn : [
20412                 {
20413                     tag : 'a',
20414                     href : this.href || '#',
20415                     cn : text
20416                 }
20417             ]
20418         };
20419         
20420         if(this.disable){
20421             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20422         }
20423         
20424         if(this.submenu){
20425             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20426             
20427             if(this.pos == 'left'){
20428                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20429             }
20430         }
20431         
20432         return cfg;
20433     },
20434     
20435     initEvents : function() 
20436     {
20437         this.el.on('mouseover', this.onMouseOver, this);
20438         this.el.on('mouseout', this.onMouseOut, this);
20439         
20440         this.el.select('a', true).first().on('click', this.onClick, this);
20441         
20442     },
20443     
20444     onClick : function(e)
20445     {
20446         if(this.preventDefault){
20447             e.preventDefault();
20448         }
20449         
20450         this.fireEvent("click", this, e);
20451     },
20452     
20453     onMouseOver : function(e)
20454     {
20455         if(this.submenu && this.pos == 'left'){
20456             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20457         }
20458         
20459         this.fireEvent("mouseover", this, e);
20460     },
20461     
20462     onMouseOut : function(e)
20463     {
20464         this.fireEvent("mouseout", this, e);
20465     }
20466 });
20467
20468  
20469
20470  /*
20471  * - LGPL
20472  *
20473  * menu separator
20474  * 
20475  */
20476 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20477
20478 /**
20479  * @class Roo.bootstrap.menu.Separator
20480  * @extends Roo.bootstrap.Component
20481  * Bootstrap Separator class
20482  * 
20483  * @constructor
20484  * Create a new Separator
20485  * @param {Object} config The config object
20486  */
20487
20488
20489 Roo.bootstrap.menu.Separator = function(config){
20490     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20491 };
20492
20493 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20494     
20495     getAutoCreate : function(){
20496         var cfg = {
20497             tag : 'li',
20498             cls: 'divider'
20499         };
20500         
20501         return cfg;
20502     }
20503    
20504 });
20505
20506  
20507
20508