Roo/form/ComboBoxArray.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     {
3724         if (typeof (this.menu) != 'undefined') {
3725             this.menu.parentType = this.xtype;
3726             this.menu.triggerEl = this.el;
3727             this.addxtype(Roo.apply({}, this.menu));
3728         }
3729         
3730         this.el.select('a',true).on('click', this.onClick, this);
3731         
3732         if(this.tagtype == 'span'){
3733             this.el.select('span',true).on('click', this.onClick, this);
3734         }
3735        
3736         // at this point parent should be available..
3737         this.parent().register(this);
3738     },
3739     
3740     onClick : function(e)
3741     {
3742          
3743         if(this.preventDefault){
3744             e.preventDefault();
3745         }
3746         if (this.disabled) {
3747             return;
3748         }
3749         
3750         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3751         if (tg && tg.transition) {
3752             Roo.log("waiting for the transitionend");
3753             return;
3754         }
3755         
3756         Roo.log("fire event clicked");
3757         if(this.fireEvent('click', this, e) === false){
3758             return;
3759         };
3760         
3761         if(this.tagtype == 'span'){
3762             return;
3763         }
3764         
3765         var p = this.parent();
3766         if (['tabs','pills'].indexOf(p.type)!==-1) {
3767             if (typeof(p.setActiveItem) !== 'undefined') {
3768                 p.setActiveItem(this);
3769             }
3770         }
3771         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3772         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3773             // remove the collapsed menu expand...
3774             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3775         }
3776         
3777     },
3778     
3779     isActive: function () {
3780         return this.active
3781     },
3782     setActive : function(state, fire, is_was_active)
3783     {
3784         if (this.active && !state & this.navId) {
3785             this.was_active = true;
3786             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3787             if (nv) {
3788                 nv.clearWasActive(this);
3789             }
3790             
3791         }
3792         this.active = state;
3793         
3794         if (!state ) {
3795             this.el.removeClass('active');
3796         } else if (!this.el.hasClass('active')) {
3797             this.el.addClass('active');
3798         }
3799         if (fire) {
3800             this.fireEvent('changed', this, state);
3801         }
3802         
3803         // show a panel if it's registered and related..
3804         
3805         if (!this.navId || !this.tabId || !state || is_was_active) {
3806             return;
3807         }
3808         
3809         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3810         if (!tg) {
3811             return;
3812         }
3813         var pan = tg.getPanelByName(this.tabId);
3814         if (!pan) {
3815             return;
3816         }
3817         // if we can not flip to new panel - go back to old nav highlight..
3818         if (false == tg.showPanel(pan)) {
3819             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3820             if (nv) {
3821                 var onav = nv.getWasActive();
3822                 if (onav) {
3823                     onav.setActive(true, false, true);
3824                 }
3825             }
3826             
3827         }
3828         
3829         
3830         
3831     },
3832      // this should not be here...
3833     setDisabled : function(state)
3834     {
3835         this.disabled = state;
3836         if (!state ) {
3837             this.el.removeClass('disabled');
3838         } else if (!this.el.hasClass('disabled')) {
3839             this.el.addClass('disabled');
3840         }
3841         
3842     }
3843 });
3844  
3845
3846  /*
3847  * - LGPL
3848  *
3849  * sidebar item
3850  *
3851  *  li
3852  *    <span> icon </span>
3853  *    <span> text </span>
3854  *    <span>badge </span>
3855  */
3856
3857 /**
3858  * @class Roo.bootstrap.NavSidebarItem
3859  * @extends Roo.bootstrap.NavItem
3860  * Bootstrap Navbar.NavSidebarItem class
3861  * @constructor
3862  * Create a new Navbar Button
3863  * @param {Object} config The config object
3864  */
3865 Roo.bootstrap.NavSidebarItem = function(config){
3866     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3867     this.addEvents({
3868         // raw events
3869         /**
3870          * @event click
3871          * The raw click event for the entire grid.
3872          * @param {Roo.EventObject} e
3873          */
3874         "click" : true,
3875          /**
3876             * @event changed
3877             * Fires when the active item active state changes
3878             * @param {Roo.bootstrap.NavSidebarItem} this
3879             * @param {boolean} state the new state
3880              
3881          */
3882         'changed': true
3883     });
3884    
3885 };
3886
3887 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3888     
3889     
3890     getAutoCreate : function(){
3891         
3892         
3893         var a = {
3894                 tag: 'a',
3895                 href : this.href || '#',
3896                 cls: '',
3897                 html : '',
3898                 cn : []
3899         };
3900         var cfg = {
3901             tag: 'li',
3902             cls: '',
3903             cn: [ a ]
3904         }
3905         var span = {
3906             tag: 'span',
3907             html : this.html || ''
3908         }
3909         
3910         
3911         if (this.active) {
3912             cfg.cls += ' active';
3913         }
3914         
3915         // left icon..
3916         if (this.glyphicon || this.icon) {
3917             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3918             a.cn.push({ tag : 'i', cls : c }) ;
3919         }
3920         // html..
3921         a.cn.push(span);
3922         // then badge..
3923         if (this.badge !== '') {
3924             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3925         }
3926         // fi
3927         if (this.menu) {
3928             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3929             a.cls += 'dropdown-toggle treeview' ;
3930             
3931         }
3932         
3933         
3934         
3935         return cfg;
3936          
3937            
3938     }
3939    
3940      
3941  
3942 });
3943  
3944
3945  /*
3946  * - LGPL
3947  *
3948  * row
3949  * 
3950  */
3951
3952 /**
3953  * @class Roo.bootstrap.Row
3954  * @extends Roo.bootstrap.Component
3955  * Bootstrap Row class (contains columns...)
3956  * 
3957  * @constructor
3958  * Create a new Row
3959  * @param {Object} config The config object
3960  */
3961
3962 Roo.bootstrap.Row = function(config){
3963     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3964 };
3965
3966 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3967     
3968     getAutoCreate : function(){
3969        return {
3970             cls: 'row clearfix'
3971        };
3972     }
3973     
3974     
3975 });
3976
3977  
3978
3979  /*
3980  * - LGPL
3981  *
3982  * element
3983  * 
3984  */
3985
3986 /**
3987  * @class Roo.bootstrap.Element
3988  * @extends Roo.bootstrap.Component
3989  * Bootstrap Element class
3990  * @cfg {String} html contents of the element
3991  * @cfg {String} tag tag of the element
3992  * @cfg {String} cls class of the element
3993  * 
3994  * @constructor
3995  * Create a new Element
3996  * @param {Object} config The config object
3997  */
3998
3999 Roo.bootstrap.Element = function(config){
4000     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4001 };
4002
4003 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4004     
4005     tag: 'div',
4006     cls: '',
4007     html: '',
4008      
4009     
4010     getAutoCreate : function(){
4011         
4012         var cfg = {
4013             tag: this.tag,
4014             cls: this.cls,
4015             html: this.html
4016         }
4017         
4018         
4019         
4020         return cfg;
4021     }
4022    
4023 });
4024
4025  
4026
4027  /*
4028  * - LGPL
4029  *
4030  * pagination
4031  * 
4032  */
4033
4034 /**
4035  * @class Roo.bootstrap.Pagination
4036  * @extends Roo.bootstrap.Component
4037  * Bootstrap Pagination class
4038  * @cfg {String} size xs | sm | md | lg
4039  * @cfg {Boolean} inverse false | true
4040  * 
4041  * @constructor
4042  * Create a new Pagination
4043  * @param {Object} config The config object
4044  */
4045
4046 Roo.bootstrap.Pagination = function(config){
4047     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4048 };
4049
4050 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4051     
4052     cls: false,
4053     size: false,
4054     inverse: false,
4055     
4056     getAutoCreate : function(){
4057         var cfg = {
4058             tag: 'ul',
4059                 cls: 'pagination'
4060         };
4061         if (this.inverse) {
4062             cfg.cls += ' inverse';
4063         }
4064         if (this.html) {
4065             cfg.html=this.html;
4066         }
4067         if (this.cls) {
4068             cfg.cls += " " + this.cls;
4069         }
4070         return cfg;
4071     }
4072    
4073 });
4074
4075  
4076
4077  /*
4078  * - LGPL
4079  *
4080  * Pagination item
4081  * 
4082  */
4083
4084
4085 /**
4086  * @class Roo.bootstrap.PaginationItem
4087  * @extends Roo.bootstrap.Component
4088  * Bootstrap PaginationItem class
4089  * @cfg {String} html text
4090  * @cfg {String} href the link
4091  * @cfg {Boolean} preventDefault (true | false) default true
4092  * @cfg {Boolean} active (true | false) default false
4093  * 
4094  * 
4095  * @constructor
4096  * Create a new PaginationItem
4097  * @param {Object} config The config object
4098  */
4099
4100
4101 Roo.bootstrap.PaginationItem = function(config){
4102     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4103     this.addEvents({
4104         // raw events
4105         /**
4106          * @event click
4107          * The raw click event for the entire grid.
4108          * @param {Roo.EventObject} e
4109          */
4110         "click" : true
4111     });
4112 };
4113
4114 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4115     
4116     href : false,
4117     html : false,
4118     preventDefault: true,
4119     active : false,
4120     cls : false,
4121     
4122     getAutoCreate : function(){
4123         var cfg= {
4124             tag: 'li',
4125             cn: [
4126                 {
4127                     tag : 'a',
4128                     href : this.href ? this.href : '#',
4129                     html : this.html ? this.html : ''
4130                 }
4131             ]
4132         };
4133         
4134         if(this.cls){
4135             cfg.cls = this.cls;
4136         }
4137         
4138         if(this.active){
4139             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4140         }
4141         
4142         return cfg;
4143     },
4144     
4145     initEvents: function() {
4146         
4147         this.el.on('click', this.onClick, this);
4148         
4149     },
4150     onClick : function(e)
4151     {
4152         Roo.log('PaginationItem on click ');
4153         if(this.preventDefault){
4154             e.preventDefault();
4155         }
4156         
4157         this.fireEvent('click', this, e);
4158     }
4159    
4160 });
4161
4162  
4163
4164  /*
4165  * - LGPL
4166  *
4167  * slider
4168  * 
4169  */
4170
4171
4172 /**
4173  * @class Roo.bootstrap.Slider
4174  * @extends Roo.bootstrap.Component
4175  * Bootstrap Slider class
4176  *    
4177  * @constructor
4178  * Create a new Slider
4179  * @param {Object} config The config object
4180  */
4181
4182 Roo.bootstrap.Slider = function(config){
4183     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4184 };
4185
4186 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4187     
4188     getAutoCreate : function(){
4189         
4190         var cfg = {
4191             tag: 'div',
4192             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4193             cn: [
4194                 {
4195                     tag: 'a',
4196                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4197                 }
4198             ]
4199         }
4200         
4201         return cfg;
4202     }
4203    
4204 });
4205
4206  /*
4207  * Based on:
4208  * Ext JS Library 1.1.1
4209  * Copyright(c) 2006-2007, Ext JS, LLC.
4210  *
4211  * Originally Released Under LGPL - original licence link has changed is not relivant.
4212  *
4213  * Fork - LGPL
4214  * <script type="text/javascript">
4215  */
4216  
4217
4218 /**
4219  * @class Roo.grid.ColumnModel
4220  * @extends Roo.util.Observable
4221  * This is the default implementation of a ColumnModel used by the Grid. It defines
4222  * the columns in the grid.
4223  * <br>Usage:<br>
4224  <pre><code>
4225  var colModel = new Roo.grid.ColumnModel([
4226         {header: "Ticker", width: 60, sortable: true, locked: true},
4227         {header: "Company Name", width: 150, sortable: true},
4228         {header: "Market Cap.", width: 100, sortable: true},
4229         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4230         {header: "Employees", width: 100, sortable: true, resizable: false}
4231  ]);
4232  </code></pre>
4233  * <p>
4234  
4235  * The config options listed for this class are options which may appear in each
4236  * individual column definition.
4237  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4238  * @constructor
4239  * @param {Object} config An Array of column config objects. See this class's
4240  * config objects for details.
4241 */
4242 Roo.grid.ColumnModel = function(config){
4243         /**
4244      * The config passed into the constructor
4245      */
4246     this.config = config;
4247     this.lookup = {};
4248
4249     // if no id, create one
4250     // if the column does not have a dataIndex mapping,
4251     // map it to the order it is in the config
4252     for(var i = 0, len = config.length; i < len; i++){
4253         var c = config[i];
4254         if(typeof c.dataIndex == "undefined"){
4255             c.dataIndex = i;
4256         }
4257         if(typeof c.renderer == "string"){
4258             c.renderer = Roo.util.Format[c.renderer];
4259         }
4260         if(typeof c.id == "undefined"){
4261             c.id = Roo.id();
4262         }
4263         if(c.editor && c.editor.xtype){
4264             c.editor  = Roo.factory(c.editor, Roo.grid);
4265         }
4266         if(c.editor && c.editor.isFormField){
4267             c.editor = new Roo.grid.GridEditor(c.editor);
4268         }
4269         this.lookup[c.id] = c;
4270     }
4271
4272     /**
4273      * The width of columns which have no width specified (defaults to 100)
4274      * @type Number
4275      */
4276     this.defaultWidth = 100;
4277
4278     /**
4279      * Default sortable of columns which have no sortable specified (defaults to false)
4280      * @type Boolean
4281      */
4282     this.defaultSortable = false;
4283
4284     this.addEvents({
4285         /**
4286              * @event widthchange
4287              * Fires when the width of a column changes.
4288              * @param {ColumnModel} this
4289              * @param {Number} columnIndex The column index
4290              * @param {Number} newWidth The new width
4291              */
4292             "widthchange": true,
4293         /**
4294              * @event headerchange
4295              * Fires when the text of a header changes.
4296              * @param {ColumnModel} this
4297              * @param {Number} columnIndex The column index
4298              * @param {Number} newText The new header text
4299              */
4300             "headerchange": true,
4301         /**
4302              * @event hiddenchange
4303              * Fires when a column is hidden or "unhidden".
4304              * @param {ColumnModel} this
4305              * @param {Number} columnIndex The column index
4306              * @param {Boolean} hidden true if hidden, false otherwise
4307              */
4308             "hiddenchange": true,
4309             /**
4310          * @event columnmoved
4311          * Fires when a column is moved.
4312          * @param {ColumnModel} this
4313          * @param {Number} oldIndex
4314          * @param {Number} newIndex
4315          */
4316         "columnmoved" : true,
4317         /**
4318          * @event columlockchange
4319          * Fires when a column's locked state is changed
4320          * @param {ColumnModel} this
4321          * @param {Number} colIndex
4322          * @param {Boolean} locked true if locked
4323          */
4324         "columnlockchange" : true
4325     });
4326     Roo.grid.ColumnModel.superclass.constructor.call(this);
4327 };
4328 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4329     /**
4330      * @cfg {String} header The header text to display in the Grid view.
4331      */
4332     /**
4333      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4334      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4335      * specified, the column's index is used as an index into the Record's data Array.
4336      */
4337     /**
4338      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4339      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4340      */
4341     /**
4342      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4343      * Defaults to the value of the {@link #defaultSortable} property.
4344      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4345      */
4346     /**
4347      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4348      */
4349     /**
4350      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4351      */
4352     /**
4353      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4354      */
4355     /**
4356      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4357      */
4358     /**
4359      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4360      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4361      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4362      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4363      */
4364        /**
4365      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4366      */
4367     /**
4368      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4369      */
4370
4371     /**
4372      * Returns the id of the column at the specified index.
4373      * @param {Number} index The column index
4374      * @return {String} the id
4375      */
4376     getColumnId : function(index){
4377         return this.config[index].id;
4378     },
4379
4380     /**
4381      * Returns the column for a specified id.
4382      * @param {String} id The column id
4383      * @return {Object} the column
4384      */
4385     getColumnById : function(id){
4386         return this.lookup[id];
4387     },
4388
4389     
4390     /**
4391      * Returns the column for a specified dataIndex.
4392      * @param {String} dataIndex The column dataIndex
4393      * @return {Object|Boolean} the column or false if not found
4394      */
4395     getColumnByDataIndex: function(dataIndex){
4396         var index = this.findColumnIndex(dataIndex);
4397         return index > -1 ? this.config[index] : false;
4398     },
4399     
4400     /**
4401      * Returns the index for a specified column id.
4402      * @param {String} id The column id
4403      * @return {Number} the index, or -1 if not found
4404      */
4405     getIndexById : function(id){
4406         for(var i = 0, len = this.config.length; i < len; i++){
4407             if(this.config[i].id == id){
4408                 return i;
4409             }
4410         }
4411         return -1;
4412     },
4413     
4414     /**
4415      * Returns the index for a specified column dataIndex.
4416      * @param {String} dataIndex The column dataIndex
4417      * @return {Number} the index, or -1 if not found
4418      */
4419     
4420     findColumnIndex : function(dataIndex){
4421         for(var i = 0, len = this.config.length; i < len; i++){
4422             if(this.config[i].dataIndex == dataIndex){
4423                 return i;
4424             }
4425         }
4426         return -1;
4427     },
4428     
4429     
4430     moveColumn : function(oldIndex, newIndex){
4431         var c = this.config[oldIndex];
4432         this.config.splice(oldIndex, 1);
4433         this.config.splice(newIndex, 0, c);
4434         this.dataMap = null;
4435         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4436     },
4437
4438     isLocked : function(colIndex){
4439         return this.config[colIndex].locked === true;
4440     },
4441
4442     setLocked : function(colIndex, value, suppressEvent){
4443         if(this.isLocked(colIndex) == value){
4444             return;
4445         }
4446         this.config[colIndex].locked = value;
4447         if(!suppressEvent){
4448             this.fireEvent("columnlockchange", this, colIndex, value);
4449         }
4450     },
4451
4452     getTotalLockedWidth : function(){
4453         var totalWidth = 0;
4454         for(var i = 0; i < this.config.length; i++){
4455             if(this.isLocked(i) && !this.isHidden(i)){
4456                 this.totalWidth += this.getColumnWidth(i);
4457             }
4458         }
4459         return totalWidth;
4460     },
4461
4462     getLockedCount : function(){
4463         for(var i = 0, len = this.config.length; i < len; i++){
4464             if(!this.isLocked(i)){
4465                 return i;
4466             }
4467         }
4468     },
4469
4470     /**
4471      * Returns the number of columns.
4472      * @return {Number}
4473      */
4474     getColumnCount : function(visibleOnly){
4475         if(visibleOnly === true){
4476             var c = 0;
4477             for(var i = 0, len = this.config.length; i < len; i++){
4478                 if(!this.isHidden(i)){
4479                     c++;
4480                 }
4481             }
4482             return c;
4483         }
4484         return this.config.length;
4485     },
4486
4487     /**
4488      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4489      * @param {Function} fn
4490      * @param {Object} scope (optional)
4491      * @return {Array} result
4492      */
4493     getColumnsBy : function(fn, scope){
4494         var r = [];
4495         for(var i = 0, len = this.config.length; i < len; i++){
4496             var c = this.config[i];
4497             if(fn.call(scope||this, c, i) === true){
4498                 r[r.length] = c;
4499             }
4500         }
4501         return r;
4502     },
4503
4504     /**
4505      * Returns true if the specified column is sortable.
4506      * @param {Number} col The column index
4507      * @return {Boolean}
4508      */
4509     isSortable : function(col){
4510         if(typeof this.config[col].sortable == "undefined"){
4511             return this.defaultSortable;
4512         }
4513         return this.config[col].sortable;
4514     },
4515
4516     /**
4517      * Returns the rendering (formatting) function defined for the column.
4518      * @param {Number} col The column index.
4519      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4520      */
4521     getRenderer : function(col){
4522         if(!this.config[col].renderer){
4523             return Roo.grid.ColumnModel.defaultRenderer;
4524         }
4525         return this.config[col].renderer;
4526     },
4527
4528     /**
4529      * Sets the rendering (formatting) function for a column.
4530      * @param {Number} col The column index
4531      * @param {Function} fn The function to use to process the cell's raw data
4532      * to return HTML markup for the grid view. The render function is called with
4533      * the following parameters:<ul>
4534      * <li>Data value.</li>
4535      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4536      * <li>css A CSS style string to apply to the table cell.</li>
4537      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4538      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4539      * <li>Row index</li>
4540      * <li>Column index</li>
4541      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4542      */
4543     setRenderer : function(col, fn){
4544         this.config[col].renderer = fn;
4545     },
4546
4547     /**
4548      * Returns the width for the specified column.
4549      * @param {Number} col The column index
4550      * @return {Number}
4551      */
4552     getColumnWidth : function(col){
4553         return this.config[col].width * 1 || this.defaultWidth;
4554     },
4555
4556     /**
4557      * Sets the width for a column.
4558      * @param {Number} col The column index
4559      * @param {Number} width The new width
4560      */
4561     setColumnWidth : function(col, width, suppressEvent){
4562         this.config[col].width = width;
4563         this.totalWidth = null;
4564         if(!suppressEvent){
4565              this.fireEvent("widthchange", this, col, width);
4566         }
4567     },
4568
4569     /**
4570      * Returns the total width of all columns.
4571      * @param {Boolean} includeHidden True to include hidden column widths
4572      * @return {Number}
4573      */
4574     getTotalWidth : function(includeHidden){
4575         if(!this.totalWidth){
4576             this.totalWidth = 0;
4577             for(var i = 0, len = this.config.length; i < len; i++){
4578                 if(includeHidden || !this.isHidden(i)){
4579                     this.totalWidth += this.getColumnWidth(i);
4580                 }
4581             }
4582         }
4583         return this.totalWidth;
4584     },
4585
4586     /**
4587      * Returns the header for the specified column.
4588      * @param {Number} col The column index
4589      * @return {String}
4590      */
4591     getColumnHeader : function(col){
4592         return this.config[col].header;
4593     },
4594
4595     /**
4596      * Sets the header for a column.
4597      * @param {Number} col The column index
4598      * @param {String} header The new header
4599      */
4600     setColumnHeader : function(col, header){
4601         this.config[col].header = header;
4602         this.fireEvent("headerchange", this, col, header);
4603     },
4604
4605     /**
4606      * Returns the tooltip for the specified column.
4607      * @param {Number} col The column index
4608      * @return {String}
4609      */
4610     getColumnTooltip : function(col){
4611             return this.config[col].tooltip;
4612     },
4613     /**
4614      * Sets the tooltip for a column.
4615      * @param {Number} col The column index
4616      * @param {String} tooltip The new tooltip
4617      */
4618     setColumnTooltip : function(col, tooltip){
4619             this.config[col].tooltip = tooltip;
4620     },
4621
4622     /**
4623      * Returns the dataIndex for the specified column.
4624      * @param {Number} col The column index
4625      * @return {Number}
4626      */
4627     getDataIndex : function(col){
4628         return this.config[col].dataIndex;
4629     },
4630
4631     /**
4632      * Sets the dataIndex for a column.
4633      * @param {Number} col The column index
4634      * @param {Number} dataIndex The new dataIndex
4635      */
4636     setDataIndex : function(col, dataIndex){
4637         this.config[col].dataIndex = dataIndex;
4638     },
4639
4640     
4641     
4642     /**
4643      * Returns true if the cell is editable.
4644      * @param {Number} colIndex The column index
4645      * @param {Number} rowIndex The row index
4646      * @return {Boolean}
4647      */
4648     isCellEditable : function(colIndex, rowIndex){
4649         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4650     },
4651
4652     /**
4653      * Returns the editor defined for the cell/column.
4654      * return false or null to disable editing.
4655      * @param {Number} colIndex The column index
4656      * @param {Number} rowIndex The row index
4657      * @return {Object}
4658      */
4659     getCellEditor : function(colIndex, rowIndex){
4660         return this.config[colIndex].editor;
4661     },
4662
4663     /**
4664      * Sets if a column is editable.
4665      * @param {Number} col The column index
4666      * @param {Boolean} editable True if the column is editable
4667      */
4668     setEditable : function(col, editable){
4669         this.config[col].editable = editable;
4670     },
4671
4672
4673     /**
4674      * Returns true if the column is hidden.
4675      * @param {Number} colIndex The column index
4676      * @return {Boolean}
4677      */
4678     isHidden : function(colIndex){
4679         return this.config[colIndex].hidden;
4680     },
4681
4682
4683     /**
4684      * Returns true if the column width cannot be changed
4685      */
4686     isFixed : function(colIndex){
4687         return this.config[colIndex].fixed;
4688     },
4689
4690     /**
4691      * Returns true if the column can be resized
4692      * @return {Boolean}
4693      */
4694     isResizable : function(colIndex){
4695         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4696     },
4697     /**
4698      * Sets if a column is hidden.
4699      * @param {Number} colIndex The column index
4700      * @param {Boolean} hidden True if the column is hidden
4701      */
4702     setHidden : function(colIndex, hidden){
4703         this.config[colIndex].hidden = hidden;
4704         this.totalWidth = null;
4705         this.fireEvent("hiddenchange", this, colIndex, hidden);
4706     },
4707
4708     /**
4709      * Sets the editor for a column.
4710      * @param {Number} col The column index
4711      * @param {Object} editor The editor object
4712      */
4713     setEditor : function(col, editor){
4714         this.config[col].editor = editor;
4715     }
4716 });
4717
4718 Roo.grid.ColumnModel.defaultRenderer = function(value){
4719         if(typeof value == "string" && value.length < 1){
4720             return "&#160;";
4721         }
4722         return value;
4723 };
4724
4725 // Alias for backwards compatibility
4726 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4727 /*
4728  * Based on:
4729  * Ext JS Library 1.1.1
4730  * Copyright(c) 2006-2007, Ext JS, LLC.
4731  *
4732  * Originally Released Under LGPL - original licence link has changed is not relivant.
4733  *
4734  * Fork - LGPL
4735  * <script type="text/javascript">
4736  */
4737  
4738 /**
4739  * @class Roo.LoadMask
4740  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4741  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4742  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4743  * element's UpdateManager load indicator and will be destroyed after the initial load.
4744  * @constructor
4745  * Create a new LoadMask
4746  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4747  * @param {Object} config The config object
4748  */
4749 Roo.LoadMask = function(el, config){
4750     this.el = Roo.get(el);
4751     Roo.apply(this, config);
4752     if(this.store){
4753         this.store.on('beforeload', this.onBeforeLoad, this);
4754         this.store.on('load', this.onLoad, this);
4755         this.store.on('loadexception', this.onLoadException, this);
4756         this.removeMask = false;
4757     }else{
4758         var um = this.el.getUpdateManager();
4759         um.showLoadIndicator = false; // disable the default indicator
4760         um.on('beforeupdate', this.onBeforeLoad, this);
4761         um.on('update', this.onLoad, this);
4762         um.on('failure', this.onLoad, this);
4763         this.removeMask = true;
4764     }
4765 };
4766
4767 Roo.LoadMask.prototype = {
4768     /**
4769      * @cfg {Boolean} removeMask
4770      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4771      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4772      */
4773     /**
4774      * @cfg {String} msg
4775      * The text to display in a centered loading message box (defaults to 'Loading...')
4776      */
4777     msg : 'Loading...',
4778     /**
4779      * @cfg {String} msgCls
4780      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4781      */
4782     msgCls : 'x-mask-loading',
4783
4784     /**
4785      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4786      * @type Boolean
4787      */
4788     disabled: false,
4789
4790     /**
4791      * Disables the mask to prevent it from being displayed
4792      */
4793     disable : function(){
4794        this.disabled = true;
4795     },
4796
4797     /**
4798      * Enables the mask so that it can be displayed
4799      */
4800     enable : function(){
4801         this.disabled = false;
4802     },
4803     
4804     onLoadException : function()
4805     {
4806         Roo.log(arguments);
4807         
4808         if (typeof(arguments[3]) != 'undefined') {
4809             Roo.MessageBox.alert("Error loading",arguments[3]);
4810         } 
4811         /*
4812         try {
4813             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4814                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4815             }   
4816         } catch(e) {
4817             
4818         }
4819         */
4820     
4821         
4822         
4823         this.el.unmask(this.removeMask);
4824     },
4825     // private
4826     onLoad : function()
4827     {
4828         this.el.unmask(this.removeMask);
4829     },
4830
4831     // private
4832     onBeforeLoad : function(){
4833         if(!this.disabled){
4834             this.el.mask(this.msg, this.msgCls);
4835         }
4836     },
4837
4838     // private
4839     destroy : function(){
4840         if(this.store){
4841             this.store.un('beforeload', this.onBeforeLoad, this);
4842             this.store.un('load', this.onLoad, this);
4843             this.store.un('loadexception', this.onLoadException, this);
4844         }else{
4845             var um = this.el.getUpdateManager();
4846             um.un('beforeupdate', this.onBeforeLoad, this);
4847             um.un('update', this.onLoad, this);
4848             um.un('failure', this.onLoad, this);
4849         }
4850     }
4851 };/*
4852  * - LGPL
4853  *
4854  * table
4855  * 
4856  */
4857
4858 /**
4859  * @class Roo.bootstrap.Table
4860  * @extends Roo.bootstrap.Component
4861  * Bootstrap Table class
4862  * @cfg {String} cls table class
4863  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4864  * @cfg {String} bgcolor Specifies the background color for a table
4865  * @cfg {Number} border Specifies whether the table cells should have borders or not
4866  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4867  * @cfg {Number} cellspacing Specifies the space between cells
4868  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4869  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4870  * @cfg {String} sortable Specifies that the table should be sortable
4871  * @cfg {String} summary Specifies a summary of the content of a table
4872  * @cfg {Number} width Specifies the width of a table
4873  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4874  * 
4875  * @cfg {boolean} striped Should the rows be alternative striped
4876  * @cfg {boolean} bordered Add borders to the table
4877  * @cfg {boolean} hover Add hover highlighting
4878  * @cfg {boolean} condensed Format condensed
4879  * @cfg {boolean} responsive Format condensed
4880  * @cfg {Boolean} loadMask (true|false) default false
4881  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4882  * @cfg {Boolean} thead (true|false) generate thead, default true
4883  * @cfg {Boolean} RowSelection (true|false) default false
4884  * @cfg {Boolean} CellSelection (true|false) default false
4885  *
4886  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4887  
4888  * 
4889  * @constructor
4890  * Create a new Table
4891  * @param {Object} config The config object
4892  */
4893
4894 Roo.bootstrap.Table = function(config){
4895     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4896     
4897     if (this.sm) {
4898         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4899         this.sm = this.selModel;
4900         this.sm.xmodule = this.xmodule || false;
4901     }
4902     if (this.cm && typeof(this.cm.config) == 'undefined') {
4903         this.colModel = new Roo.grid.ColumnModel(this.cm);
4904         this.cm = this.colModel;
4905         this.cm.xmodule = this.xmodule || false;
4906     }
4907     if (this.store) {
4908         this.store= Roo.factory(this.store, Roo.data);
4909         this.ds = this.store;
4910         this.ds.xmodule = this.xmodule || false;
4911          
4912     }
4913     if (this.footer && this.store) {
4914         this.footer.dataSource = this.ds;
4915         this.footer = Roo.factory(this.footer);
4916     }
4917     
4918     /** @private */
4919     this.addEvents({
4920         /**
4921          * @event cellclick
4922          * Fires when a cell is clicked
4923          * @param {Roo.bootstrap.Table} this
4924          * @param {Roo.Element} el
4925          * @param {Number} rowIndex
4926          * @param {Number} columnIndex
4927          * @param {Roo.EventObject} e
4928          */
4929         "cellclick" : true,
4930         /**
4931          * @event celldblclick
4932          * Fires when a cell is double clicked
4933          * @param {Roo.bootstrap.Table} this
4934          * @param {Roo.Element} el
4935          * @param {Number} rowIndex
4936          * @param {Number} columnIndex
4937          * @param {Roo.EventObject} e
4938          */
4939         "celldblclick" : true,
4940         /**
4941          * @event rowclick
4942          * Fires when a row is clicked
4943          * @param {Roo.bootstrap.Table} this
4944          * @param {Roo.Element} el
4945          * @param {Number} rowIndex
4946          * @param {Roo.EventObject} e
4947          */
4948         "rowclick" : true,
4949         /**
4950          * @event rowdblclick
4951          * Fires when a row is double clicked
4952          * @param {Roo.bootstrap.Table} this
4953          * @param {Roo.Element} el
4954          * @param {Number} rowIndex
4955          * @param {Roo.EventObject} e
4956          */
4957         "rowdblclick" : true,
4958         /**
4959          * @event mouseover
4960          * Fires when a mouseover occur
4961          * @param {Roo.bootstrap.Table} this
4962          * @param {Roo.Element} el
4963          * @param {Number} rowIndex
4964          * @param {Number} columnIndex
4965          * @param {Roo.EventObject} e
4966          */
4967         "mouseover" : true,
4968         /**
4969          * @event mouseout
4970          * Fires when a mouseout occur
4971          * @param {Roo.bootstrap.Table} this
4972          * @param {Roo.Element} el
4973          * @param {Number} rowIndex
4974          * @param {Number} columnIndex
4975          * @param {Roo.EventObject} e
4976          */
4977         "mouseout" : true,
4978         /**
4979          * @event rowclass
4980          * Fires when a row is rendered, so you can change add a style to it.
4981          * @param {Roo.bootstrap.Table} this
4982          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4983          */
4984         'rowclass' : true
4985         
4986     });
4987 };
4988
4989 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4990     
4991     cls: false,
4992     align: false,
4993     bgcolor: false,
4994     border: false,
4995     cellpadding: false,
4996     cellspacing: false,
4997     frame: false,
4998     rules: false,
4999     sortable: false,
5000     summary: false,
5001     width: false,
5002     striped : false,
5003     bordered: false,
5004     hover:  false,
5005     condensed : false,
5006     responsive : false,
5007     sm : false,
5008     cm : false,
5009     store : false,
5010     loadMask : false,
5011     tfoot : true,
5012     thead : true,
5013     RowSelection : false,
5014     CellSelection : false,
5015     layout : false,
5016     
5017     // Roo.Element - the tbody
5018     mainBody: false, 
5019     
5020     getAutoCreate : function(){
5021         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5022         
5023         cfg = {
5024             tag: 'table',
5025             cls : 'table',
5026             cn : []
5027         }
5028             
5029         if (this.striped) {
5030             cfg.cls += ' table-striped';
5031         }
5032         
5033         if (this.hover) {
5034             cfg.cls += ' table-hover';
5035         }
5036         if (this.bordered) {
5037             cfg.cls += ' table-bordered';
5038         }
5039         if (this.condensed) {
5040             cfg.cls += ' table-condensed';
5041         }
5042         if (this.responsive) {
5043             cfg.cls += ' table-responsive';
5044         }
5045         
5046         if (this.cls) {
5047             cfg.cls+=  ' ' +this.cls;
5048         }
5049         
5050         // this lot should be simplifed...
5051         
5052         if (this.align) {
5053             cfg.align=this.align;
5054         }
5055         if (this.bgcolor) {
5056             cfg.bgcolor=this.bgcolor;
5057         }
5058         if (this.border) {
5059             cfg.border=this.border;
5060         }
5061         if (this.cellpadding) {
5062             cfg.cellpadding=this.cellpadding;
5063         }
5064         if (this.cellspacing) {
5065             cfg.cellspacing=this.cellspacing;
5066         }
5067         if (this.frame) {
5068             cfg.frame=this.frame;
5069         }
5070         if (this.rules) {
5071             cfg.rules=this.rules;
5072         }
5073         if (this.sortable) {
5074             cfg.sortable=this.sortable;
5075         }
5076         if (this.summary) {
5077             cfg.summary=this.summary;
5078         }
5079         if (this.width) {
5080             cfg.width=this.width;
5081         }
5082         if (this.layout) {
5083             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5084         }
5085         
5086         if(this.store || this.cm){
5087             if(this.thead){
5088                 cfg.cn.push(this.renderHeader());
5089             }
5090             
5091             cfg.cn.push(this.renderBody());
5092             
5093             if(this.tfoot){
5094                 cfg.cn.push(this.renderFooter());
5095             }
5096             
5097             cfg.cls+=  ' TableGrid';
5098         }
5099         
5100         return { cn : [ cfg ] };
5101     },
5102     
5103     initEvents : function()
5104     {   
5105         if(!this.store || !this.cm){
5106             return;
5107         }
5108         
5109         //Roo.log('initEvents with ds!!!!');
5110         
5111         this.mainBody = this.el.select('tbody', true).first();
5112         
5113         
5114         var _this = this;
5115         
5116         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5117             e.on('click', _this.sort, _this);
5118         });
5119         
5120         this.el.on("click", this.onClick, this);
5121         this.el.on("dblclick", this.onDblClick, this);
5122         
5123         this.parent().el.setStyle('position', 'relative');
5124         if (this.footer) {
5125             this.footer.parentId = this.id;
5126             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5127         }
5128         
5129         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5130         
5131         this.store.on('load', this.onLoad, this);
5132         this.store.on('beforeload', this.onBeforeLoad, this);
5133         this.store.on('update', this.onUpdate, this);
5134         
5135     },
5136     
5137     onMouseover : function(e, el)
5138     {
5139         var cell = Roo.get(el);
5140         
5141         if(!cell){
5142             return;
5143         }
5144         
5145         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5146             cell = cell.findParent('td', false, true);
5147         }
5148         
5149         var row = cell.findParent('tr', false, true);
5150         var cellIndex = cell.dom.cellIndex;
5151         var rowIndex = row.dom.rowIndex - 1; // start from 0
5152         
5153         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5154         
5155     },
5156     
5157     onMouseout : function(e, el)
5158     {
5159         var cell = Roo.get(el);
5160         
5161         if(!cell){
5162             return;
5163         }
5164         
5165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5166             cell = cell.findParent('td', false, true);
5167         }
5168         
5169         var row = cell.findParent('tr', false, true);
5170         var cellIndex = cell.dom.cellIndex;
5171         var rowIndex = row.dom.rowIndex - 1; // start from 0
5172         
5173         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5174         
5175     },
5176     
5177     onClick : function(e, el)
5178     {
5179         var cell = Roo.get(el);
5180         
5181         if(!cell || (!this.CellSelection && !this.RowSelection)){
5182             return;
5183         }
5184         
5185         
5186         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5187             cell = cell.findParent('td', false, true);
5188         }
5189         
5190         var row = cell.findParent('tr', false, true);
5191         var cellIndex = cell.dom.cellIndex;
5192         var rowIndex = row.dom.rowIndex - 1;
5193         
5194         if(this.CellSelection){
5195             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5196         }
5197         
5198         if(this.RowSelection){
5199             this.fireEvent('rowclick', this, row, rowIndex, e);
5200         }
5201         
5202         
5203     },
5204     
5205     onDblClick : function(e,el)
5206     {
5207         var cell = Roo.get(el);
5208         
5209         if(!cell || (!this.CellSelection && !this.RowSelection)){
5210             return;
5211         }
5212         
5213         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5214             cell = cell.findParent('td', false, true);
5215         }
5216         
5217         var row = cell.findParent('tr', false, true);
5218         var cellIndex = cell.dom.cellIndex;
5219         var rowIndex = row.dom.rowIndex - 1;
5220         
5221         if(this.CellSelection){
5222             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5223         }
5224         
5225         if(this.RowSelection){
5226             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5227         }
5228     },
5229     
5230     sort : function(e,el)
5231     {
5232         var col = Roo.get(el)
5233         
5234         if(!col.hasClass('sortable')){
5235             return;
5236         }
5237         
5238         var sort = col.attr('sort');
5239         var dir = 'ASC';
5240         
5241         if(col.hasClass('glyphicon-arrow-up')){
5242             dir = 'DESC';
5243         }
5244         
5245         this.store.sortInfo = {field : sort, direction : dir};
5246         
5247         if (this.footer) {
5248             Roo.log("calling footer first");
5249             this.footer.onClick('first');
5250         } else {
5251         
5252             this.store.load({ params : { start : 0 } });
5253         }
5254     },
5255     
5256     renderHeader : function()
5257     {
5258         var header = {
5259             tag: 'thead',
5260             cn : []
5261         };
5262         
5263         var cm = this.cm;
5264         
5265         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5266             
5267             var config = cm.config[i];
5268                     
5269             var c = {
5270                 tag: 'th',
5271                 style : '',
5272                 html: cm.getColumnHeader(i)
5273             };
5274             
5275             if(typeof(config.hidden) != 'undefined' && config.hidden){
5276                 c.style += ' display:none;';
5277             }
5278             
5279             if(typeof(config.dataIndex) != 'undefined'){
5280                 c.sort = config.dataIndex;
5281             }
5282             
5283             if(typeof(config.sortable) != 'undefined' && config.sortable){
5284                 c.cls = 'sortable';
5285             }
5286             
5287             if(typeof(config.align) != 'undefined' && config.align.length){
5288                 c.style += ' text-align:' + config.align + ';';
5289             }
5290             
5291             if(typeof(config.width) != 'undefined'){
5292                 c.style += ' width:' + config.width + 'px;';
5293             }
5294             
5295             header.cn.push(c)
5296         }
5297         
5298         return header;
5299     },
5300     
5301     renderBody : function()
5302     {
5303         var body = {
5304             tag: 'tbody',
5305             cn : [
5306                 {
5307                     tag: 'tr',
5308                     cn : [
5309                         {
5310                             tag : 'td',
5311                             colspan :  this.cm.getColumnCount()
5312                         }
5313                     ]
5314                 }
5315             ]
5316         };
5317         
5318         return body;
5319     },
5320     
5321     renderFooter : function()
5322     {
5323         var footer = {
5324             tag: 'tfoot',
5325             cn : [
5326                 {
5327                     tag: 'tr',
5328                     cn : [
5329                         {
5330                             tag : 'td',
5331                             colspan :  this.cm.getColumnCount()
5332                         }
5333                     ]
5334                 }
5335             ]
5336         };
5337         
5338         return footer;
5339     },
5340     
5341     
5342     
5343     onLoad : function()
5344     {
5345         Roo.log('ds onload');
5346         this.clear();
5347         
5348         var _this = this;
5349         var cm = this.cm;
5350         var ds = this.store;
5351         
5352         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5353             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5354             
5355             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5356                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5357             }
5358             
5359             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5360                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5361             }
5362         });
5363         
5364         var tbody =  this.mainBody;
5365               
5366         if(ds.getCount() > 0){
5367             ds.data.each(function(d,rowIndex){
5368                 var row =  this.renderRow(cm, ds, rowIndex);
5369                 
5370                 tbody.createChild(row);
5371                 
5372                 var _this = this;
5373                 
5374                 if(row.cellObjects.length){
5375                     Roo.each(row.cellObjects, function(r){
5376                         _this.renderCellObject(r);
5377                     })
5378                 }
5379                 
5380             }, this);
5381         }
5382         
5383         Roo.each(this.el.select('tbody td', true).elements, function(e){
5384             e.on('mouseover', _this.onMouseover, _this);
5385         });
5386         
5387         Roo.each(this.el.select('tbody td', true).elements, function(e){
5388             e.on('mouseout', _this.onMouseout, _this);
5389         });
5390
5391         //if(this.loadMask){
5392         //    this.maskEl.hide();
5393         //}
5394     },
5395     
5396     
5397     onUpdate : function(ds,record)
5398     {
5399         this.refreshRow(record);
5400     },
5401     onRemove : function(ds, record, index, isUpdate){
5402         if(isUpdate !== true){
5403             this.fireEvent("beforerowremoved", this, index, record);
5404         }
5405         var bt = this.mainBody.dom;
5406         if(bt.rows[index]){
5407             bt.removeChild(bt.rows[index]);
5408         }
5409         
5410         if(isUpdate !== true){
5411             //this.stripeRows(index);
5412             //this.syncRowHeights(index, index);
5413             //this.layout();
5414             this.fireEvent("rowremoved", this, index, record);
5415         }
5416     },
5417     
5418     
5419     refreshRow : function(record){
5420         var ds = this.store, index;
5421         if(typeof record == 'number'){
5422             index = record;
5423             record = ds.getAt(index);
5424         }else{
5425             index = ds.indexOf(record);
5426         }
5427         this.insertRow(ds, index, true);
5428         this.onRemove(ds, record, index+1, true);
5429         //this.syncRowHeights(index, index);
5430         //this.layout();
5431         this.fireEvent("rowupdated", this, index, record);
5432     },
5433     
5434     insertRow : function(dm, rowIndex, isUpdate){
5435         
5436         if(!isUpdate){
5437             this.fireEvent("beforerowsinserted", this, rowIndex);
5438         }
5439             //var s = this.getScrollState();
5440         var row = this.renderRow(this.cm, this.store, rowIndex);
5441         // insert before rowIndex..
5442         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5443         
5444         var _this = this;
5445                 
5446         if(row.cellObjects.length){
5447             Roo.each(row.cellObjects, function(r){
5448                 _this.renderCellObject(r);
5449             })
5450         }
5451             
5452         if(!isUpdate){
5453             this.fireEvent("rowsinserted", this, rowIndex);
5454             //this.syncRowHeights(firstRow, lastRow);
5455             //this.stripeRows(firstRow);
5456             //this.layout();
5457         }
5458         
5459     },
5460     
5461     
5462     getRowDom : function(rowIndex)
5463     {
5464         // not sure if I need to check this.. but let's do it anyway..
5465         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5466                 this.mainBody.dom.rows[rowIndex] : false
5467     },
5468     // returns the object tree for a tr..
5469   
5470     
5471     renderRow : function(cm, ds, rowIndex) {
5472         
5473         var d = ds.getAt(rowIndex);
5474         
5475         var row = {
5476             tag : 'tr',
5477             cn : []
5478         };
5479             
5480         var cellObjects = [];
5481         
5482         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5483             var config = cm.config[i];
5484             
5485             var renderer = cm.getRenderer(i);
5486             var value = '';
5487             var id = false;
5488             
5489             if(typeof(renderer) !== 'undefined'){
5490                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5491             }
5492             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5493             // and are rendered into the cells after the row is rendered - using the id for the element.
5494             
5495             if(typeof(value) === 'object'){
5496                 id = Roo.id();
5497                 cellObjects.push({
5498                     container : id,
5499                     cfg : value 
5500                 })
5501             }
5502             
5503             var rowcfg = {
5504                 record: d,
5505                 rowIndex : rowIndex,
5506                 colIndex : i,
5507                 rowClass : ''
5508             }
5509
5510             this.fireEvent('rowclass', this, rowcfg);
5511             
5512             var td = {
5513                 tag: 'td',
5514                 cls : rowcfg.rowClass,
5515                 style: '',
5516                 html: (typeof(value) === 'object') ? '' : value
5517             };
5518             
5519             if (id) {
5520                 td.id = id;
5521             }
5522             
5523             if(typeof(config.hidden) != 'undefined' && config.hidden){
5524                 td.style += ' display:none;';
5525             }
5526             
5527             if(typeof(config.align) != 'undefined' && config.align.length){
5528                 td.style += ' text-align:' + config.align + ';';
5529             }
5530             
5531             if(typeof(config.width) != 'undefined'){
5532                 td.style += ' width:' +  config.width + 'px;';
5533             }
5534              
5535             row.cn.push(td);
5536            
5537         }
5538         
5539         row.cellObjects = cellObjects;
5540         
5541         return row;
5542           
5543     },
5544     
5545     
5546     
5547     onBeforeLoad : function()
5548     {
5549         //Roo.log('ds onBeforeLoad');
5550         
5551         //this.clear();
5552         
5553         //if(this.loadMask){
5554         //    this.maskEl.show();
5555         //}
5556     },
5557     
5558     clear : function()
5559     {
5560         this.el.select('tbody', true).first().dom.innerHTML = '';
5561     },
5562     
5563     getSelectionModel : function(){
5564         if(!this.selModel){
5565             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5566         }
5567         return this.selModel;
5568     },
5569     /*
5570      * Render the Roo.bootstrap object from renderder
5571      */
5572     renderCellObject : function(r)
5573     {
5574         var _this = this;
5575         
5576         var t = r.cfg.render(r.container);
5577         
5578         if(r.cfg.cn){
5579             Roo.each(r.cfg.cn, function(c){
5580                 var child = {
5581                     container: t.getChildContainer(),
5582                     cfg: c
5583                 }
5584                 _this.renderCellObject(child);
5585             })
5586         }
5587     }
5588    
5589 });
5590
5591  
5592
5593  /*
5594  * - LGPL
5595  *
5596  * table cell
5597  * 
5598  */
5599
5600 /**
5601  * @class Roo.bootstrap.TableCell
5602  * @extends Roo.bootstrap.Component
5603  * Bootstrap TableCell class
5604  * @cfg {String} html cell contain text
5605  * @cfg {String} cls cell class
5606  * @cfg {String} tag cell tag (td|th) default td
5607  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5608  * @cfg {String} align Aligns the content in a cell
5609  * @cfg {String} axis Categorizes cells
5610  * @cfg {String} bgcolor Specifies the background color of a cell
5611  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5612  * @cfg {Number} colspan Specifies the number of columns a cell should span
5613  * @cfg {String} headers Specifies one or more header cells a cell is related to
5614  * @cfg {Number} height Sets the height of a cell
5615  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5616  * @cfg {Number} rowspan Sets the number of rows a cell should span
5617  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5618  * @cfg {String} valign Vertical aligns the content in a cell
5619  * @cfg {Number} width Specifies the width of a cell
5620  * 
5621  * @constructor
5622  * Create a new TableCell
5623  * @param {Object} config The config object
5624  */
5625
5626 Roo.bootstrap.TableCell = function(config){
5627     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5628 };
5629
5630 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5631     
5632     html: false,
5633     cls: false,
5634     tag: false,
5635     abbr: false,
5636     align: false,
5637     axis: false,
5638     bgcolor: false,
5639     charoff: false,
5640     colspan: false,
5641     headers: false,
5642     height: false,
5643     nowrap: false,
5644     rowspan: false,
5645     scope: false,
5646     valign: false,
5647     width: false,
5648     
5649     
5650     getAutoCreate : function(){
5651         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5652         
5653         cfg = {
5654             tag: 'td'
5655         }
5656         
5657         if(this.tag){
5658             cfg.tag = this.tag;
5659         }
5660         
5661         if (this.html) {
5662             cfg.html=this.html
5663         }
5664         if (this.cls) {
5665             cfg.cls=this.cls
5666         }
5667         if (this.abbr) {
5668             cfg.abbr=this.abbr
5669         }
5670         if (this.align) {
5671             cfg.align=this.align
5672         }
5673         if (this.axis) {
5674             cfg.axis=this.axis
5675         }
5676         if (this.bgcolor) {
5677             cfg.bgcolor=this.bgcolor
5678         }
5679         if (this.charoff) {
5680             cfg.charoff=this.charoff
5681         }
5682         if (this.colspan) {
5683             cfg.colspan=this.colspan
5684         }
5685         if (this.headers) {
5686             cfg.headers=this.headers
5687         }
5688         if (this.height) {
5689             cfg.height=this.height
5690         }
5691         if (this.nowrap) {
5692             cfg.nowrap=this.nowrap
5693         }
5694         if (this.rowspan) {
5695             cfg.rowspan=this.rowspan
5696         }
5697         if (this.scope) {
5698             cfg.scope=this.scope
5699         }
5700         if (this.valign) {
5701             cfg.valign=this.valign
5702         }
5703         if (this.width) {
5704             cfg.width=this.width
5705         }
5706         
5707         
5708         return cfg;
5709     }
5710    
5711 });
5712
5713  
5714
5715  /*
5716  * - LGPL
5717  *
5718  * table row
5719  * 
5720  */
5721
5722 /**
5723  * @class Roo.bootstrap.TableRow
5724  * @extends Roo.bootstrap.Component
5725  * Bootstrap TableRow class
5726  * @cfg {String} cls row class
5727  * @cfg {String} align Aligns the content in a table row
5728  * @cfg {String} bgcolor Specifies a background color for a table row
5729  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5730  * @cfg {String} valign Vertical aligns the content in a table row
5731  * 
5732  * @constructor
5733  * Create a new TableRow
5734  * @param {Object} config The config object
5735  */
5736
5737 Roo.bootstrap.TableRow = function(config){
5738     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5739 };
5740
5741 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5742     
5743     cls: false,
5744     align: false,
5745     bgcolor: false,
5746     charoff: false,
5747     valign: false,
5748     
5749     getAutoCreate : function(){
5750         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5751         
5752         cfg = {
5753             tag: 'tr'
5754         }
5755             
5756         if(this.cls){
5757             cfg.cls = this.cls;
5758         }
5759         if(this.align){
5760             cfg.align = this.align;
5761         }
5762         if(this.bgcolor){
5763             cfg.bgcolor = this.bgcolor;
5764         }
5765         if(this.charoff){
5766             cfg.charoff = this.charoff;
5767         }
5768         if(this.valign){
5769             cfg.valign = this.valign;
5770         }
5771         
5772         return cfg;
5773     }
5774    
5775 });
5776
5777  
5778
5779  /*
5780  * - LGPL
5781  *
5782  * table body
5783  * 
5784  */
5785
5786 /**
5787  * @class Roo.bootstrap.TableBody
5788  * @extends Roo.bootstrap.Component
5789  * Bootstrap TableBody class
5790  * @cfg {String} cls element class
5791  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5792  * @cfg {String} align Aligns the content inside the element
5793  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5794  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5795  * 
5796  * @constructor
5797  * Create a new TableBody
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.TableBody = function(config){
5802     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5803 };
5804
5805 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5806     
5807     cls: false,
5808     tag: false,
5809     align: false,
5810     charoff: false,
5811     valign: false,
5812     
5813     getAutoCreate : function(){
5814         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5815         
5816         cfg = {
5817             tag: 'tbody'
5818         }
5819             
5820         if (this.cls) {
5821             cfg.cls=this.cls
5822         }
5823         if(this.tag){
5824             cfg.tag = this.tag;
5825         }
5826         
5827         if(this.align){
5828             cfg.align = this.align;
5829         }
5830         if(this.charoff){
5831             cfg.charoff = this.charoff;
5832         }
5833         if(this.valign){
5834             cfg.valign = this.valign;
5835         }
5836         
5837         return cfg;
5838     }
5839     
5840     
5841 //    initEvents : function()
5842 //    {
5843 //        
5844 //        if(!this.store){
5845 //            return;
5846 //        }
5847 //        
5848 //        this.store = Roo.factory(this.store, Roo.data);
5849 //        this.store.on('load', this.onLoad, this);
5850 //        
5851 //        this.store.load();
5852 //        
5853 //    },
5854 //    
5855 //    onLoad: function () 
5856 //    {   
5857 //        this.fireEvent('load', this);
5858 //    }
5859 //    
5860 //   
5861 });
5862
5863  
5864
5865  /*
5866  * Based on:
5867  * Ext JS Library 1.1.1
5868  * Copyright(c) 2006-2007, Ext JS, LLC.
5869  *
5870  * Originally Released Under LGPL - original licence link has changed is not relivant.
5871  *
5872  * Fork - LGPL
5873  * <script type="text/javascript">
5874  */
5875
5876 // as we use this in bootstrap.
5877 Roo.namespace('Roo.form');
5878  /**
5879  * @class Roo.form.Action
5880  * Internal Class used to handle form actions
5881  * @constructor
5882  * @param {Roo.form.BasicForm} el The form element or its id
5883  * @param {Object} config Configuration options
5884  */
5885
5886  
5887  
5888 // define the action interface
5889 Roo.form.Action = function(form, options){
5890     this.form = form;
5891     this.options = options || {};
5892 };
5893 /**
5894  * Client Validation Failed
5895  * @const 
5896  */
5897 Roo.form.Action.CLIENT_INVALID = 'client';
5898 /**
5899  * Server Validation Failed
5900  * @const 
5901  */
5902 Roo.form.Action.SERVER_INVALID = 'server';
5903  /**
5904  * Connect to Server Failed
5905  * @const 
5906  */
5907 Roo.form.Action.CONNECT_FAILURE = 'connect';
5908 /**
5909  * Reading Data from Server Failed
5910  * @const 
5911  */
5912 Roo.form.Action.LOAD_FAILURE = 'load';
5913
5914 Roo.form.Action.prototype = {
5915     type : 'default',
5916     failureType : undefined,
5917     response : undefined,
5918     result : undefined,
5919
5920     // interface method
5921     run : function(options){
5922
5923     },
5924
5925     // interface method
5926     success : function(response){
5927
5928     },
5929
5930     // interface method
5931     handleResponse : function(response){
5932
5933     },
5934
5935     // default connection failure
5936     failure : function(response){
5937         
5938         this.response = response;
5939         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5940         this.form.afterAction(this, false);
5941     },
5942
5943     processResponse : function(response){
5944         this.response = response;
5945         if(!response.responseText){
5946             return true;
5947         }
5948         this.result = this.handleResponse(response);
5949         return this.result;
5950     },
5951
5952     // utility functions used internally
5953     getUrl : function(appendParams){
5954         var url = this.options.url || this.form.url || this.form.el.dom.action;
5955         if(appendParams){
5956             var p = this.getParams();
5957             if(p){
5958                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5959             }
5960         }
5961         return url;
5962     },
5963
5964     getMethod : function(){
5965         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5966     },
5967
5968     getParams : function(){
5969         var bp = this.form.baseParams;
5970         var p = this.options.params;
5971         if(p){
5972             if(typeof p == "object"){
5973                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5974             }else if(typeof p == 'string' && bp){
5975                 p += '&' + Roo.urlEncode(bp);
5976             }
5977         }else if(bp){
5978             p = Roo.urlEncode(bp);
5979         }
5980         return p;
5981     },
5982
5983     createCallback : function(){
5984         return {
5985             success: this.success,
5986             failure: this.failure,
5987             scope: this,
5988             timeout: (this.form.timeout*1000),
5989             upload: this.form.fileUpload ? this.success : undefined
5990         };
5991     }
5992 };
5993
5994 Roo.form.Action.Submit = function(form, options){
5995     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5996 };
5997
5998 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5999     type : 'submit',
6000
6001     haveProgress : false,
6002     uploadComplete : false,
6003     
6004     // uploadProgress indicator.
6005     uploadProgress : function()
6006     {
6007         if (!this.form.progressUrl) {
6008             return;
6009         }
6010         
6011         if (!this.haveProgress) {
6012             Roo.MessageBox.progress("Uploading", "Uploading");
6013         }
6014         if (this.uploadComplete) {
6015            Roo.MessageBox.hide();
6016            return;
6017         }
6018         
6019         this.haveProgress = true;
6020    
6021         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6022         
6023         var c = new Roo.data.Connection();
6024         c.request({
6025             url : this.form.progressUrl,
6026             params: {
6027                 id : uid
6028             },
6029             method: 'GET',
6030             success : function(req){
6031                //console.log(data);
6032                 var rdata = false;
6033                 var edata;
6034                 try  {
6035                    rdata = Roo.decode(req.responseText)
6036                 } catch (e) {
6037                     Roo.log("Invalid data from server..");
6038                     Roo.log(edata);
6039                     return;
6040                 }
6041                 if (!rdata || !rdata.success) {
6042                     Roo.log(rdata);
6043                     Roo.MessageBox.alert(Roo.encode(rdata));
6044                     return;
6045                 }
6046                 var data = rdata.data;
6047                 
6048                 if (this.uploadComplete) {
6049                    Roo.MessageBox.hide();
6050                    return;
6051                 }
6052                    
6053                 if (data){
6054                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6055                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6056                     );
6057                 }
6058                 this.uploadProgress.defer(2000,this);
6059             },
6060        
6061             failure: function(data) {
6062                 Roo.log('progress url failed ');
6063                 Roo.log(data);
6064             },
6065             scope : this
6066         });
6067            
6068     },
6069     
6070     
6071     run : function()
6072     {
6073         // run get Values on the form, so it syncs any secondary forms.
6074         this.form.getValues();
6075         
6076         var o = this.options;
6077         var method = this.getMethod();
6078         var isPost = method == 'POST';
6079         if(o.clientValidation === false || this.form.isValid()){
6080             
6081             if (this.form.progressUrl) {
6082                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6083                     (new Date() * 1) + '' + Math.random());
6084                     
6085             } 
6086             
6087             
6088             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6089                 form:this.form.el.dom,
6090                 url:this.getUrl(!isPost),
6091                 method: method,
6092                 params:isPost ? this.getParams() : null,
6093                 isUpload: this.form.fileUpload
6094             }));
6095             
6096             this.uploadProgress();
6097
6098         }else if (o.clientValidation !== false){ // client validation failed
6099             this.failureType = Roo.form.Action.CLIENT_INVALID;
6100             this.form.afterAction(this, false);
6101         }
6102     },
6103
6104     success : function(response)
6105     {
6106         this.uploadComplete= true;
6107         if (this.haveProgress) {
6108             Roo.MessageBox.hide();
6109         }
6110         
6111         
6112         var result = this.processResponse(response);
6113         if(result === true || result.success){
6114             this.form.afterAction(this, true);
6115             return;
6116         }
6117         if(result.errors){
6118             this.form.markInvalid(result.errors);
6119             this.failureType = Roo.form.Action.SERVER_INVALID;
6120         }
6121         this.form.afterAction(this, false);
6122     },
6123     failure : function(response)
6124     {
6125         this.uploadComplete= true;
6126         if (this.haveProgress) {
6127             Roo.MessageBox.hide();
6128         }
6129         
6130         this.response = response;
6131         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6132         this.form.afterAction(this, false);
6133     },
6134     
6135     handleResponse : function(response){
6136         if(this.form.errorReader){
6137             var rs = this.form.errorReader.read(response);
6138             var errors = [];
6139             if(rs.records){
6140                 for(var i = 0, len = rs.records.length; i < len; i++) {
6141                     var r = rs.records[i];
6142                     errors[i] = r.data;
6143                 }
6144             }
6145             if(errors.length < 1){
6146                 errors = null;
6147             }
6148             return {
6149                 success : rs.success,
6150                 errors : errors
6151             };
6152         }
6153         var ret = false;
6154         try {
6155             ret = Roo.decode(response.responseText);
6156         } catch (e) {
6157             ret = {
6158                 success: false,
6159                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6160                 errors : []
6161             };
6162         }
6163         return ret;
6164         
6165     }
6166 });
6167
6168
6169 Roo.form.Action.Load = function(form, options){
6170     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6171     this.reader = this.form.reader;
6172 };
6173
6174 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6175     type : 'load',
6176
6177     run : function(){
6178         
6179         Roo.Ajax.request(Roo.apply(
6180                 this.createCallback(), {
6181                     method:this.getMethod(),
6182                     url:this.getUrl(false),
6183                     params:this.getParams()
6184         }));
6185     },
6186
6187     success : function(response){
6188         
6189         var result = this.processResponse(response);
6190         if(result === true || !result.success || !result.data){
6191             this.failureType = Roo.form.Action.LOAD_FAILURE;
6192             this.form.afterAction(this, false);
6193             return;
6194         }
6195         this.form.clearInvalid();
6196         this.form.setValues(result.data);
6197         this.form.afterAction(this, true);
6198     },
6199
6200     handleResponse : function(response){
6201         if(this.form.reader){
6202             var rs = this.form.reader.read(response);
6203             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6204             return {
6205                 success : rs.success,
6206                 data : data
6207             };
6208         }
6209         return Roo.decode(response.responseText);
6210     }
6211 });
6212
6213 Roo.form.Action.ACTION_TYPES = {
6214     'load' : Roo.form.Action.Load,
6215     'submit' : Roo.form.Action.Submit
6216 };/*
6217  * - LGPL
6218  *
6219  * form
6220  * 
6221  */
6222
6223 /**
6224  * @class Roo.bootstrap.Form
6225  * @extends Roo.bootstrap.Component
6226  * Bootstrap Form class
6227  * @cfg {String} method  GET | POST (default POST)
6228  * @cfg {String} labelAlign top | left (default top)
6229  * @cfg {String} align left  | right - for navbars
6230  * @cfg {Boolean} loadMask load mask when submit (default true)
6231
6232  * 
6233  * @constructor
6234  * Create a new Form
6235  * @param {Object} config The config object
6236  */
6237
6238
6239 Roo.bootstrap.Form = function(config){
6240     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6241     this.addEvents({
6242         /**
6243          * @event clientvalidation
6244          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6245          * @param {Form} this
6246          * @param {Boolean} valid true if the form has passed client-side validation
6247          */
6248         clientvalidation: true,
6249         /**
6250          * @event beforeaction
6251          * Fires before any action is performed. Return false to cancel the action.
6252          * @param {Form} this
6253          * @param {Action} action The action to be performed
6254          */
6255         beforeaction: true,
6256         /**
6257          * @event actionfailed
6258          * Fires when an action fails.
6259          * @param {Form} this
6260          * @param {Action} action The action that failed
6261          */
6262         actionfailed : true,
6263         /**
6264          * @event actioncomplete
6265          * Fires when an action is completed.
6266          * @param {Form} this
6267          * @param {Action} action The action that completed
6268          */
6269         actioncomplete : true
6270     });
6271     
6272 };
6273
6274 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6275       
6276      /**
6277      * @cfg {String} method
6278      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6279      */
6280     method : 'POST',
6281     /**
6282      * @cfg {String} url
6283      * The URL to use for form actions if one isn't supplied in the action options.
6284      */
6285     /**
6286      * @cfg {Boolean} fileUpload
6287      * Set to true if this form is a file upload.
6288      */
6289      
6290     /**
6291      * @cfg {Object} baseParams
6292      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6293      */
6294       
6295     /**
6296      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6297      */
6298     timeout: 30,
6299     /**
6300      * @cfg {Sting} align (left|right) for navbar forms
6301      */
6302     align : 'left',
6303
6304     // private
6305     activeAction : null,
6306  
6307     /**
6308      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6309      * element by passing it or its id or mask the form itself by passing in true.
6310      * @type Mixed
6311      */
6312     waitMsgTarget : false,
6313     
6314     loadMask : true,
6315     
6316     getAutoCreate : function(){
6317         
6318         var cfg = {
6319             tag: 'form',
6320             method : this.method || 'POST',
6321             id : this.id || Roo.id(),
6322             cls : ''
6323         }
6324         if (this.parent().xtype.match(/^Nav/)) {
6325             cfg.cls = 'navbar-form navbar-' + this.align;
6326             
6327         }
6328         
6329         if (this.labelAlign == 'left' ) {
6330             cfg.cls += ' form-horizontal';
6331         }
6332         
6333         
6334         return cfg;
6335     },
6336     initEvents : function()
6337     {
6338         this.el.on('submit', this.onSubmit, this);
6339         // this was added as random key presses on the form where triggering form submit.
6340         this.el.on('keypress', function(e) {
6341             if (e.getCharCode() != 13) {
6342                 return true;
6343             }
6344             // we might need to allow it for textareas.. and some other items.
6345             // check e.getTarget().
6346             
6347             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6348                 return true;
6349             }
6350         
6351             Roo.log("keypress blocked");
6352             
6353             e.preventDefault();
6354             return false;
6355         });
6356         
6357     },
6358     // private
6359     onSubmit : function(e){
6360         e.stopEvent();
6361     },
6362     
6363      /**
6364      * Returns true if client-side validation on the form is successful.
6365      * @return Boolean
6366      */
6367     isValid : function(){
6368         var items = this.getItems();
6369         var valid = true;
6370         items.each(function(f){
6371            if(!f.validate()){
6372                valid = false;
6373                
6374            }
6375         });
6376         return valid;
6377     },
6378     /**
6379      * Returns true if any fields in this form have changed since their original load.
6380      * @return Boolean
6381      */
6382     isDirty : function(){
6383         var dirty = false;
6384         var items = this.getItems();
6385         items.each(function(f){
6386            if(f.isDirty()){
6387                dirty = true;
6388                return false;
6389            }
6390            return true;
6391         });
6392         return dirty;
6393     },
6394      /**
6395      * Performs a predefined action (submit or load) or custom actions you define on this form.
6396      * @param {String} actionName The name of the action type
6397      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6398      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6399      * accept other config options):
6400      * <pre>
6401 Property          Type             Description
6402 ----------------  ---------------  ----------------------------------------------------------------------------------
6403 url               String           The url for the action (defaults to the form's url)
6404 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6405 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6406 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6407                                    validate the form on the client (defaults to false)
6408      * </pre>
6409      * @return {BasicForm} this
6410      */
6411     doAction : function(action, options){
6412         if(typeof action == 'string'){
6413             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6414         }
6415         if(this.fireEvent('beforeaction', this, action) !== false){
6416             this.beforeAction(action);
6417             action.run.defer(100, action);
6418         }
6419         return this;
6420     },
6421     
6422     // private
6423     beforeAction : function(action){
6424         var o = action.options;
6425         
6426         if(this.loadMask){
6427             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6428         }
6429         // not really supported yet.. ??
6430         
6431         //if(this.waitMsgTarget === true){
6432         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6433         //}else if(this.waitMsgTarget){
6434         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6435         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6436         //}else {
6437         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6438        // }
6439          
6440     },
6441
6442     // private
6443     afterAction : function(action, success){
6444         this.activeAction = null;
6445         var o = action.options;
6446         
6447         //if(this.waitMsgTarget === true){
6448             this.el.unmask();
6449         //}else if(this.waitMsgTarget){
6450         //    this.waitMsgTarget.unmask();
6451         //}else{
6452         //    Roo.MessageBox.updateProgress(1);
6453         //    Roo.MessageBox.hide();
6454        // }
6455         // 
6456         if(success){
6457             if(o.reset){
6458                 this.reset();
6459             }
6460             Roo.callback(o.success, o.scope, [this, action]);
6461             this.fireEvent('actioncomplete', this, action);
6462             
6463         }else{
6464             
6465             // failure condition..
6466             // we have a scenario where updates need confirming.
6467             // eg. if a locking scenario exists..
6468             // we look for { errors : { needs_confirm : true }} in the response.
6469             if (
6470                 (typeof(action.result) != 'undefined')  &&
6471                 (typeof(action.result.errors) != 'undefined')  &&
6472                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6473            ){
6474                 var _t = this;
6475                 Roo.log("not supported yet");
6476                  /*
6477                 
6478                 Roo.MessageBox.confirm(
6479                     "Change requires confirmation",
6480                     action.result.errorMsg,
6481                     function(r) {
6482                         if (r != 'yes') {
6483                             return;
6484                         }
6485                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6486                     }
6487                     
6488                 );
6489                 */
6490                 
6491                 
6492                 return;
6493             }
6494             
6495             Roo.callback(o.failure, o.scope, [this, action]);
6496             // show an error message if no failed handler is set..
6497             if (!this.hasListener('actionfailed')) {
6498                 Roo.log("need to add dialog support");
6499                 /*
6500                 Roo.MessageBox.alert("Error",
6501                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6502                         action.result.errorMsg :
6503                         "Saving Failed, please check your entries or try again"
6504                 );
6505                 */
6506             }
6507             
6508             this.fireEvent('actionfailed', this, action);
6509         }
6510         
6511     },
6512     /**
6513      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6514      * @param {String} id The value to search for
6515      * @return Field
6516      */
6517     findField : function(id){
6518         var items = this.getItems();
6519         var field = items.get(id);
6520         if(!field){
6521              items.each(function(f){
6522                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6523                     field = f;
6524                     return false;
6525                 }
6526                 return true;
6527             });
6528         }
6529         return field || null;
6530     },
6531      /**
6532      * Mark fields in this form invalid in bulk.
6533      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6534      * @return {BasicForm} this
6535      */
6536     markInvalid : function(errors){
6537         if(errors instanceof Array){
6538             for(var i = 0, len = errors.length; i < len; i++){
6539                 var fieldError = errors[i];
6540                 var f = this.findField(fieldError.id);
6541                 if(f){
6542                     f.markInvalid(fieldError.msg);
6543                 }
6544             }
6545         }else{
6546             var field, id;
6547             for(id in errors){
6548                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6549                     field.markInvalid(errors[id]);
6550                 }
6551             }
6552         }
6553         //Roo.each(this.childForms || [], function (f) {
6554         //    f.markInvalid(errors);
6555         //});
6556         
6557         return this;
6558     },
6559
6560     /**
6561      * Set values for fields in this form in bulk.
6562      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6563      * @return {BasicForm} this
6564      */
6565     setValues : function(values){
6566         if(values instanceof Array){ // array of objects
6567             for(var i = 0, len = values.length; i < len; i++){
6568                 var v = values[i];
6569                 var f = this.findField(v.id);
6570                 if(f){
6571                     f.setValue(v.value);
6572                     if(this.trackResetOnLoad){
6573                         f.originalValue = f.getValue();
6574                     }
6575                 }
6576             }
6577         }else{ // object hash
6578             var field, id;
6579             for(id in values){
6580                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6581                     
6582                     if (field.setFromData && 
6583                         field.valueField && 
6584                         field.displayField &&
6585                         // combos' with local stores can 
6586                         // be queried via setValue()
6587                         // to set their value..
6588                         (field.store && !field.store.isLocal)
6589                         ) {
6590                         // it's a combo
6591                         var sd = { };
6592                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6593                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6594                         field.setFromData(sd);
6595                         
6596                     } else {
6597                         field.setValue(values[id]);
6598                     }
6599                     
6600                     
6601                     if(this.trackResetOnLoad){
6602                         field.originalValue = field.getValue();
6603                     }
6604                 }
6605             }
6606         }
6607          
6608         //Roo.each(this.childForms || [], function (f) {
6609         //    f.setValues(values);
6610         //});
6611                 
6612         return this;
6613     },
6614
6615     /**
6616      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6617      * they are returned as an array.
6618      * @param {Boolean} asString
6619      * @return {Object}
6620      */
6621     getValues : function(asString){
6622         //if (this.childForms) {
6623             // copy values from the child forms
6624         //    Roo.each(this.childForms, function (f) {
6625         //        this.setValues(f.getValues());
6626         //    }, this);
6627         //}
6628         
6629         
6630         
6631         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6632         if(asString === true){
6633             return fs;
6634         }
6635         return Roo.urlDecode(fs);
6636     },
6637     
6638     /**
6639      * Returns the fields in this form as an object with key/value pairs. 
6640      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6641      * @return {Object}
6642      */
6643     getFieldValues : function(with_hidden)
6644     {
6645         var items = this.getItems();
6646         var ret = {};
6647         items.each(function(f){
6648             if (!f.getName()) {
6649                 return;
6650             }
6651             var v = f.getValue();
6652             if (f.inputType =='radio') {
6653                 if (typeof(ret[f.getName()]) == 'undefined') {
6654                     ret[f.getName()] = ''; // empty..
6655                 }
6656                 
6657                 if (!f.el.dom.checked) {
6658                     return;
6659                     
6660                 }
6661                 v = f.el.dom.value;
6662                 
6663             }
6664             
6665             // not sure if this supported any more..
6666             if ((typeof(v) == 'object') && f.getRawValue) {
6667                 v = f.getRawValue() ; // dates..
6668             }
6669             // combo boxes where name != hiddenName...
6670             if (f.name != f.getName()) {
6671                 ret[f.name] = f.getRawValue();
6672             }
6673             ret[f.getName()] = v;
6674         });
6675         
6676         return ret;
6677     },
6678
6679     /**
6680      * Clears all invalid messages in this form.
6681      * @return {BasicForm} this
6682      */
6683     clearInvalid : function(){
6684         var items = this.getItems();
6685         
6686         items.each(function(f){
6687            f.clearInvalid();
6688         });
6689         
6690         
6691         
6692         return this;
6693     },
6694
6695     /**
6696      * Resets this form.
6697      * @return {BasicForm} this
6698      */
6699     reset : function(){
6700         var items = this.getItems();
6701         items.each(function(f){
6702             f.reset();
6703         });
6704         
6705         Roo.each(this.childForms || [], function (f) {
6706             f.reset();
6707         });
6708        
6709         
6710         return this;
6711     },
6712     getItems : function()
6713     {
6714         var r=new Roo.util.MixedCollection(false, function(o){
6715             return o.id || (o.id = Roo.id());
6716         });
6717         var iter = function(el) {
6718             if (el.inputEl) {
6719                 r.add(el);
6720             }
6721             if (!el.items) {
6722                 return;
6723             }
6724             Roo.each(el.items,function(e) {
6725                 iter(e);
6726             });
6727             
6728             
6729         };
6730         iter(this);
6731         return r;
6732         
6733         
6734         
6735         
6736     }
6737     
6738 });
6739
6740  
6741 /*
6742  * Based on:
6743  * Ext JS Library 1.1.1
6744  * Copyright(c) 2006-2007, Ext JS, LLC.
6745  *
6746  * Originally Released Under LGPL - original licence link has changed is not relivant.
6747  *
6748  * Fork - LGPL
6749  * <script type="text/javascript">
6750  */
6751 /**
6752  * @class Roo.form.VTypes
6753  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6754  * @singleton
6755  */
6756 Roo.form.VTypes = function(){
6757     // closure these in so they are only created once.
6758     var alpha = /^[a-zA-Z_]+$/;
6759     var alphanum = /^[a-zA-Z0-9_]+$/;
6760     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6761     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6762
6763     // All these messages and functions are configurable
6764     return {
6765         /**
6766          * The function used to validate email addresses
6767          * @param {String} value The email address
6768          */
6769         'email' : function(v){
6770             return email.test(v);
6771         },
6772         /**
6773          * The error text to display when the email validation function returns false
6774          * @type String
6775          */
6776         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6777         /**
6778          * The keystroke filter mask to be applied on email input
6779          * @type RegExp
6780          */
6781         'emailMask' : /[a-z0-9_\.\-@]/i,
6782
6783         /**
6784          * The function used to validate URLs
6785          * @param {String} value The URL
6786          */
6787         'url' : function(v){
6788             return url.test(v);
6789         },
6790         /**
6791          * The error text to display when the url validation function returns false
6792          * @type String
6793          */
6794         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6795         
6796         /**
6797          * The function used to validate alpha values
6798          * @param {String} value The value
6799          */
6800         'alpha' : function(v){
6801             return alpha.test(v);
6802         },
6803         /**
6804          * The error text to display when the alpha validation function returns false
6805          * @type String
6806          */
6807         'alphaText' : 'This field should only contain letters and _',
6808         /**
6809          * The keystroke filter mask to be applied on alpha input
6810          * @type RegExp
6811          */
6812         'alphaMask' : /[a-z_]/i,
6813
6814         /**
6815          * The function used to validate alphanumeric values
6816          * @param {String} value The value
6817          */
6818         'alphanum' : function(v){
6819             return alphanum.test(v);
6820         },
6821         /**
6822          * The error text to display when the alphanumeric validation function returns false
6823          * @type String
6824          */
6825         'alphanumText' : 'This field should only contain letters, numbers and _',
6826         /**
6827          * The keystroke filter mask to be applied on alphanumeric input
6828          * @type RegExp
6829          */
6830         'alphanumMask' : /[a-z0-9_]/i
6831     };
6832 }();/*
6833  * - LGPL
6834  *
6835  * Input
6836  * 
6837  */
6838
6839 /**
6840  * @class Roo.bootstrap.Input
6841  * @extends Roo.bootstrap.Component
6842  * Bootstrap Input class
6843  * @cfg {Boolean} disabled is it disabled
6844  * @cfg {String} fieldLabel - the label associated
6845  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6846  * @cfg {String} name name of the input
6847  * @cfg {string} fieldLabel - the label associated
6848  * @cfg {string}  inputType - input / file submit ...
6849  * @cfg {string} placeholder - placeholder to put in text.
6850  * @cfg {string}  before - input group add on before
6851  * @cfg {string} after - input group add on after
6852  * @cfg {string} size - (lg|sm) or leave empty..
6853  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6854  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6855  * @cfg {Number} md colspan out of 12 for computer-sized screens
6856  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6857  * @cfg {string} value default value of the input
6858  * @cfg {Number} labelWidth set the width of label (0-12)
6859  * @cfg {String} labelAlign (top|left)
6860  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6861  * @cfg {String} align (left|center|right) Default left
6862  * 
6863  * 
6864  * @constructor
6865  * Create a new Input
6866  * @param {Object} config The config object
6867  */
6868
6869 Roo.bootstrap.Input = function(config){
6870     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6871    
6872         this.addEvents({
6873             /**
6874              * @event focus
6875              * Fires when this field receives input focus.
6876              * @param {Roo.form.Field} this
6877              */
6878             focus : true,
6879             /**
6880              * @event blur
6881              * Fires when this field loses input focus.
6882              * @param {Roo.form.Field} this
6883              */
6884             blur : true,
6885             /**
6886              * @event specialkey
6887              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6888              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6889              * @param {Roo.form.Field} this
6890              * @param {Roo.EventObject} e The event object
6891              */
6892             specialkey : true,
6893             /**
6894              * @event change
6895              * Fires just before the field blurs if the field value has changed.
6896              * @param {Roo.form.Field} this
6897              * @param {Mixed} newValue The new value
6898              * @param {Mixed} oldValue The original value
6899              */
6900             change : true,
6901             /**
6902              * @event invalid
6903              * Fires after the field has been marked as invalid.
6904              * @param {Roo.form.Field} this
6905              * @param {String} msg The validation message
6906              */
6907             invalid : true,
6908             /**
6909              * @event valid
6910              * Fires after the field has been validated with no errors.
6911              * @param {Roo.form.Field} this
6912              */
6913             valid : true,
6914              /**
6915              * @event keyup
6916              * Fires after the key up
6917              * @param {Roo.form.Field} this
6918              * @param {Roo.EventObject}  e The event Object
6919              */
6920             keyup : true
6921         });
6922 };
6923
6924 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6925      /**
6926      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6927       automatic validation (defaults to "keyup").
6928      */
6929     validationEvent : "keyup",
6930      /**
6931      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6932      */
6933     validateOnBlur : true,
6934     /**
6935      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6936      */
6937     validationDelay : 250,
6938      /**
6939      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6940      */
6941     focusClass : "x-form-focus",  // not needed???
6942     
6943        
6944     /**
6945      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6946      */
6947     invalidClass : "has-error",
6948     
6949     /**
6950      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6951      */
6952     selectOnFocus : false,
6953     
6954      /**
6955      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6956      */
6957     maskRe : null,
6958        /**
6959      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6960      */
6961     vtype : null,
6962     
6963       /**
6964      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6965      */
6966     disableKeyFilter : false,
6967     
6968        /**
6969      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6970      */
6971     disabled : false,
6972      /**
6973      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6974      */
6975     allowBlank : true,
6976     /**
6977      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6978      */
6979     blankText : "This field is required",
6980     
6981      /**
6982      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6983      */
6984     minLength : 0,
6985     /**
6986      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6987      */
6988     maxLength : Number.MAX_VALUE,
6989     /**
6990      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6991      */
6992     minLengthText : "The minimum length for this field is {0}",
6993     /**
6994      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6995      */
6996     maxLengthText : "The maximum length for this field is {0}",
6997   
6998     
6999     /**
7000      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7001      * If available, this function will be called only after the basic validators all return true, and will be passed the
7002      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7003      */
7004     validator : null,
7005     /**
7006      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7007      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7008      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7009      */
7010     regex : null,
7011     /**
7012      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7013      */
7014     regexText : "",
7015     
7016     
7017     
7018     fieldLabel : '',
7019     inputType : 'text',
7020     
7021     name : false,
7022     placeholder: false,
7023     before : false,
7024     after : false,
7025     size : false,
7026     // private
7027     hasFocus : false,
7028     preventMark: false,
7029     isFormField : true,
7030     value : '',
7031     labelWidth : 2,
7032     labelAlign : false,
7033     readOnly : false,
7034     align : false,
7035     formatedValue : false,
7036     
7037     parentLabelAlign : function()
7038     {
7039         var parent = this;
7040         while (parent.parent()) {
7041             parent = parent.parent();
7042             if (typeof(parent.labelAlign) !='undefined') {
7043                 return parent.labelAlign;
7044             }
7045         }
7046         return 'left';
7047         
7048     },
7049     
7050     getAutoCreate : function(){
7051         
7052         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7053         
7054         var id = Roo.id();
7055         
7056         var cfg = {};
7057         
7058         if(this.inputType != 'hidden'){
7059             cfg.cls = 'form-group' //input-group
7060         }
7061         
7062         var input =  {
7063             tag: 'input',
7064             id : id,
7065             type : this.inputType,
7066             value : this.value,
7067             cls : 'form-control',
7068             placeholder : this.placeholder || ''
7069             
7070         };
7071         
7072         if(this.align){
7073             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7074         }
7075         
7076         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7077             input.maxLength = this.maxLength;
7078         }
7079         
7080         if (this.disabled) {
7081             input.disabled=true;
7082         }
7083         
7084         if (this.readOnly) {
7085             input.readonly=true;
7086         }
7087         
7088         if (this.name) {
7089             input.name = this.name;
7090         }
7091         if (this.size) {
7092             input.cls += ' input-' + this.size;
7093         }
7094         var settings=this;
7095         ['xs','sm','md','lg'].map(function(size){
7096             if (settings[size]) {
7097                 cfg.cls += ' col-' + size + '-' + settings[size];
7098             }
7099         });
7100         
7101         var inputblock = input;
7102         
7103         if (this.before || this.after) {
7104             
7105             inputblock = {
7106                 cls : 'input-group',
7107                 cn :  [] 
7108             };
7109             if (this.before && typeof(this.before) == 'string') {
7110                 
7111                 inputblock.cn.push({
7112                     tag :'span',
7113                     cls : 'roo-input-before input-group-addon',
7114                     html : this.before
7115                 });
7116             }
7117             if (this.before && typeof(this.before) == 'object') {
7118                 this.before = Roo.factory(this.before);
7119                 Roo.log(this.before);
7120                 inputblock.cn.push({
7121                     tag :'span',
7122                     cls : 'roo-input-before input-group-' +
7123                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7124                 });
7125             }
7126             
7127             inputblock.cn.push(input);
7128             
7129             if (this.after && typeof(this.after) == 'string') {
7130                 inputblock.cn.push({
7131                     tag :'span',
7132                     cls : 'roo-input-after input-group-addon',
7133                     html : this.after
7134                 });
7135             }
7136             if (this.after && typeof(this.after) == 'object') {
7137                 this.after = Roo.factory(this.after);
7138                 Roo.log(this.after);
7139                 inputblock.cn.push({
7140                     tag :'span',
7141                     cls : 'roo-input-after input-group-' +
7142                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7143                 });
7144             }
7145         };
7146         
7147         if (align ==='left' && this.fieldLabel.length) {
7148                 Roo.log("left and has label");
7149                 cfg.cn = [
7150                     
7151                     {
7152                         tag: 'label',
7153                         'for' :  id,
7154                         cls : 'control-label col-sm-' + this.labelWidth,
7155                         html : this.fieldLabel
7156                         
7157                     },
7158                     {
7159                         cls : "col-sm-" + (12 - this.labelWidth), 
7160                         cn: [
7161                             inputblock
7162                         ]
7163                     }
7164                     
7165                 ];
7166         } else if ( this.fieldLabel.length) {
7167                 Roo.log(" label");
7168                  cfg.cn = [
7169                    
7170                     {
7171                         tag: 'label',
7172                         //cls : 'input-group-addon',
7173                         html : this.fieldLabel
7174                         
7175                     },
7176                     
7177                     inputblock
7178                     
7179                 ];
7180
7181         } else {
7182             
7183                 Roo.log(" no label && no align");
7184                 cfg.cn = [
7185                     
7186                         inputblock
7187                     
7188                 ];
7189                 
7190                 
7191         };
7192         Roo.log('input-parentType: ' + this.parentType);
7193         
7194         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7195            cfg.cls += ' navbar-form';
7196            Roo.log(cfg);
7197         }
7198         
7199         return cfg;
7200         
7201     },
7202     /**
7203      * return the real input element.
7204      */
7205     inputEl: function ()
7206     {
7207         return this.el.select('input.form-control',true).first();
7208     },
7209     setDisabled : function(v)
7210     {
7211         var i  = this.inputEl().dom;
7212         if (!v) {
7213             i.removeAttribute('disabled');
7214             return;
7215             
7216         }
7217         i.setAttribute('disabled','true');
7218     },
7219     initEvents : function()
7220     {
7221         
7222         this.inputEl().on("keydown" , this.fireKey,  this);
7223         this.inputEl().on("focus", this.onFocus,  this);
7224         this.inputEl().on("blur", this.onBlur,  this);
7225         
7226         this.inputEl().relayEvent('keyup', this);
7227
7228         // reference to original value for reset
7229         this.originalValue = this.getValue();
7230         //Roo.form.TextField.superclass.initEvents.call(this);
7231         if(this.validationEvent == 'keyup'){
7232             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7233             this.inputEl().on('keyup', this.filterValidation, this);
7234         }
7235         else if(this.validationEvent !== false){
7236             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7237         }
7238         
7239         if(this.selectOnFocus){
7240             this.on("focus", this.preFocus, this);
7241             
7242         }
7243         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7244             this.inputEl().on("keypress", this.filterKeys, this);
7245         }
7246        /* if(this.grow){
7247             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7248             this.el.on("click", this.autoSize,  this);
7249         }
7250         */
7251         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7252             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7253         }
7254         
7255         if (typeof(this.before) == 'object') {
7256             this.before.render(this.el.select('.roo-input-before',true).first());
7257         }
7258         if (typeof(this.after) == 'object') {
7259             this.after.render(this.el.select('.roo-input-after',true).first());
7260         }
7261         
7262         
7263     },
7264     filterValidation : function(e){
7265         if(!e.isNavKeyPress()){
7266             this.validationTask.delay(this.validationDelay);
7267         }
7268     },
7269      /**
7270      * Validates the field value
7271      * @return {Boolean} True if the value is valid, else false
7272      */
7273     validate : function(){
7274         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7275         if(this.disabled || this.validateValue(this.getRawValue())){
7276             this.clearInvalid();
7277             return true;
7278         }
7279         return false;
7280     },
7281     
7282     
7283     /**
7284      * Validates a value according to the field's validation rules and marks the field as invalid
7285      * if the validation fails
7286      * @param {Mixed} value The value to validate
7287      * @return {Boolean} True if the value is valid, else false
7288      */
7289     validateValue : function(value){
7290         if(value.length < 1)  { // if it's blank
7291              if(this.allowBlank){
7292                 this.clearInvalid();
7293                 return true;
7294              }else{
7295                 this.markInvalid(this.blankText);
7296                 return false;
7297              }
7298         }
7299         if(value.length < this.minLength){
7300             this.markInvalid(String.format(this.minLengthText, this.minLength));
7301             return false;
7302         }
7303         if(value.length > this.maxLength){
7304             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7305             return false;
7306         }
7307         if(this.vtype){
7308             var vt = Roo.form.VTypes;
7309             if(!vt[this.vtype](value, this)){
7310                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7311                 return false;
7312             }
7313         }
7314         if(typeof this.validator == "function"){
7315             var msg = this.validator(value);
7316             if(msg !== true){
7317                 this.markInvalid(msg);
7318                 return false;
7319             }
7320         }
7321         if(this.regex && !this.regex.test(value)){
7322             this.markInvalid(this.regexText);
7323             return false;
7324         }
7325         return true;
7326     },
7327
7328     
7329     
7330      // private
7331     fireKey : function(e){
7332         //Roo.log('field ' + e.getKey());
7333         if(e.isNavKeyPress()){
7334             this.fireEvent("specialkey", this, e);
7335         }
7336     },
7337     focus : function (selectText){
7338         if(this.rendered){
7339             this.inputEl().focus();
7340             if(selectText === true){
7341                 this.inputEl().dom.select();
7342             }
7343         }
7344         return this;
7345     } ,
7346     
7347     onFocus : function(){
7348         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7349            // this.el.addClass(this.focusClass);
7350         }
7351         if(!this.hasFocus){
7352             this.hasFocus = true;
7353             this.startValue = this.getValue();
7354             this.fireEvent("focus", this);
7355         }
7356     },
7357     
7358     beforeBlur : Roo.emptyFn,
7359
7360     
7361     // private
7362     onBlur : function(){
7363         this.beforeBlur();
7364         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7365             //this.el.removeClass(this.focusClass);
7366         }
7367         this.hasFocus = false;
7368         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7369             this.validate();
7370         }
7371         var v = this.getValue();
7372         if(String(v) !== String(this.startValue)){
7373             this.fireEvent('change', this, v, this.startValue);
7374         }
7375         this.fireEvent("blur", this);
7376     },
7377     
7378     /**
7379      * Resets the current field value to the originally loaded value and clears any validation messages
7380      */
7381     reset : function(){
7382         this.setValue(this.originalValue);
7383         this.clearInvalid();
7384     },
7385      /**
7386      * Returns the name of the field
7387      * @return {Mixed} name The name field
7388      */
7389     getName: function(){
7390         return this.name;
7391     },
7392      /**
7393      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7394      * @return {Mixed} value The field value
7395      */
7396     getValue : function(){
7397         
7398         var v = this.inputEl().getValue();
7399         
7400         return v;
7401     },
7402     /**
7403      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7404      * @return {Mixed} value The field value
7405      */
7406     getRawValue : function(){
7407         var v = this.inputEl().getValue();
7408         
7409         return v;
7410     },
7411     
7412     /**
7413      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7414      * @param {Mixed} value The value to set
7415      */
7416     setRawValue : function(v){
7417         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7418     },
7419     
7420     selectText : function(start, end){
7421         var v = this.getRawValue();
7422         if(v.length > 0){
7423             start = start === undefined ? 0 : start;
7424             end = end === undefined ? v.length : end;
7425             var d = this.inputEl().dom;
7426             if(d.setSelectionRange){
7427                 d.setSelectionRange(start, end);
7428             }else if(d.createTextRange){
7429                 var range = d.createTextRange();
7430                 range.moveStart("character", start);
7431                 range.moveEnd("character", v.length-end);
7432                 range.select();
7433             }
7434         }
7435     },
7436     
7437     /**
7438      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7439      * @param {Mixed} value The value to set
7440      */
7441     setValue : function(v){
7442         this.value = v;
7443         if(this.rendered){
7444             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7445             this.validate();
7446         }
7447     },
7448     
7449     /*
7450     processValue : function(value){
7451         if(this.stripCharsRe){
7452             var newValue = value.replace(this.stripCharsRe, '');
7453             if(newValue !== value){
7454                 this.setRawValue(newValue);
7455                 return newValue;
7456             }
7457         }
7458         return value;
7459     },
7460   */
7461     preFocus : function(){
7462         
7463         if(this.selectOnFocus){
7464             this.inputEl().dom.select();
7465         }
7466     },
7467     filterKeys : function(e){
7468         var k = e.getKey();
7469         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7470             return;
7471         }
7472         var c = e.getCharCode(), cc = String.fromCharCode(c);
7473         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7474             return;
7475         }
7476         if(!this.maskRe.test(cc)){
7477             e.stopEvent();
7478         }
7479     },
7480      /**
7481      * Clear any invalid styles/messages for this field
7482      */
7483     clearInvalid : function(){
7484         
7485         if(!this.el || this.preventMark){ // not rendered
7486             return;
7487         }
7488         this.el.removeClass(this.invalidClass);
7489         /*
7490         switch(this.msgTarget){
7491             case 'qtip':
7492                 this.el.dom.qtip = '';
7493                 break;
7494             case 'title':
7495                 this.el.dom.title = '';
7496                 break;
7497             case 'under':
7498                 if(this.errorEl){
7499                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7500                 }
7501                 break;
7502             case 'side':
7503                 if(this.errorIcon){
7504                     this.errorIcon.dom.qtip = '';
7505                     this.errorIcon.hide();
7506                     this.un('resize', this.alignErrorIcon, this);
7507                 }
7508                 break;
7509             default:
7510                 var t = Roo.getDom(this.msgTarget);
7511                 t.innerHTML = '';
7512                 t.style.display = 'none';
7513                 break;
7514         }
7515         */
7516         this.fireEvent('valid', this);
7517     },
7518      /**
7519      * Mark this field as invalid
7520      * @param {String} msg The validation message
7521      */
7522     markInvalid : function(msg){
7523         if(!this.el  || this.preventMark){ // not rendered
7524             return;
7525         }
7526         this.el.addClass(this.invalidClass);
7527         /*
7528         msg = msg || this.invalidText;
7529         switch(this.msgTarget){
7530             case 'qtip':
7531                 this.el.dom.qtip = msg;
7532                 this.el.dom.qclass = 'x-form-invalid-tip';
7533                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7534                     Roo.QuickTips.enable();
7535                 }
7536                 break;
7537             case 'title':
7538                 this.el.dom.title = msg;
7539                 break;
7540             case 'under':
7541                 if(!this.errorEl){
7542                     var elp = this.el.findParent('.x-form-element', 5, true);
7543                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7544                     this.errorEl.setWidth(elp.getWidth(true)-20);
7545                 }
7546                 this.errorEl.update(msg);
7547                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7548                 break;
7549             case 'side':
7550                 if(!this.errorIcon){
7551                     var elp = this.el.findParent('.x-form-element', 5, true);
7552                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7553                 }
7554                 this.alignErrorIcon();
7555                 this.errorIcon.dom.qtip = msg;
7556                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7557                 this.errorIcon.show();
7558                 this.on('resize', this.alignErrorIcon, this);
7559                 break;
7560             default:
7561                 var t = Roo.getDom(this.msgTarget);
7562                 t.innerHTML = msg;
7563                 t.style.display = this.msgDisplay;
7564                 break;
7565         }
7566         */
7567         this.fireEvent('invalid', this, msg);
7568     },
7569     // private
7570     SafariOnKeyDown : function(event)
7571     {
7572         // this is a workaround for a password hang bug on chrome/ webkit.
7573         
7574         var isSelectAll = false;
7575         
7576         if(this.inputEl().dom.selectionEnd > 0){
7577             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7578         }
7579         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7580             event.preventDefault();
7581             this.setValue('');
7582             return;
7583         }
7584         
7585         if(isSelectAll){ // backspace and delete key
7586             
7587             event.preventDefault();
7588             // this is very hacky as keydown always get's upper case.
7589             //
7590             var cc = String.fromCharCode(event.getCharCode());
7591             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7592             
7593         }
7594     },
7595     adjustWidth : function(tag, w){
7596         tag = tag.toLowerCase();
7597         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7598             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7599                 if(tag == 'input'){
7600                     return w + 2;
7601                 }
7602                 if(tag == 'textarea'){
7603                     return w-2;
7604                 }
7605             }else if(Roo.isOpera){
7606                 if(tag == 'input'){
7607                     return w + 2;
7608                 }
7609                 if(tag == 'textarea'){
7610                     return w-2;
7611                 }
7612             }
7613         }
7614         return w;
7615     }
7616     
7617 });
7618
7619  
7620 /*
7621  * - LGPL
7622  *
7623  * Input
7624  * 
7625  */
7626
7627 /**
7628  * @class Roo.bootstrap.TextArea
7629  * @extends Roo.bootstrap.Input
7630  * Bootstrap TextArea class
7631  * @cfg {Number} cols Specifies the visible width of a text area
7632  * @cfg {Number} rows Specifies the visible number of lines in a text area
7633  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7634  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7635  * @cfg {string} html text
7636  * 
7637  * @constructor
7638  * Create a new TextArea
7639  * @param {Object} config The config object
7640  */
7641
7642 Roo.bootstrap.TextArea = function(config){
7643     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7644    
7645 };
7646
7647 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7648      
7649     cols : false,
7650     rows : 5,
7651     readOnly : false,
7652     warp : 'soft',
7653     resize : false,
7654     value: false,
7655     html: false,
7656     
7657     getAutoCreate : function(){
7658         
7659         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7660         
7661         var id = Roo.id();
7662         
7663         var cfg = {};
7664         
7665         var input =  {
7666             tag: 'textarea',
7667             id : id,
7668             warp : this.warp,
7669             rows : this.rows,
7670             value : this.value || '',
7671             html: this.html || '',
7672             cls : 'form-control',
7673             placeholder : this.placeholder || '' 
7674             
7675         };
7676         
7677         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7678             input.maxLength = this.maxLength;
7679         }
7680         
7681         if(this.resize){
7682             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7683         }
7684         
7685         if(this.cols){
7686             input.cols = this.cols;
7687         }
7688         
7689         if (this.readOnly) {
7690             input.readonly = true;
7691         }
7692         
7693         if (this.name) {
7694             input.name = this.name;
7695         }
7696         
7697         if (this.size) {
7698             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7699         }
7700         
7701         var settings=this;
7702         ['xs','sm','md','lg'].map(function(size){
7703             if (settings[size]) {
7704                 cfg.cls += ' col-' + size + '-' + settings[size];
7705             }
7706         });
7707         
7708         var inputblock = input;
7709         
7710         if (this.before || this.after) {
7711             
7712             inputblock = {
7713                 cls : 'input-group',
7714                 cn :  [] 
7715             };
7716             if (this.before) {
7717                 inputblock.cn.push({
7718                     tag :'span',
7719                     cls : 'input-group-addon',
7720                     html : this.before
7721                 });
7722             }
7723             inputblock.cn.push(input);
7724             if (this.after) {
7725                 inputblock.cn.push({
7726                     tag :'span',
7727                     cls : 'input-group-addon',
7728                     html : this.after
7729                 });
7730             }
7731             
7732         }
7733         
7734         if (align ==='left' && this.fieldLabel.length) {
7735                 Roo.log("left and has label");
7736                 cfg.cn = [
7737                     
7738                     {
7739                         tag: 'label',
7740                         'for' :  id,
7741                         cls : 'control-label col-sm-' + this.labelWidth,
7742                         html : this.fieldLabel
7743                         
7744                     },
7745                     {
7746                         cls : "col-sm-" + (12 - this.labelWidth), 
7747                         cn: [
7748                             inputblock
7749                         ]
7750                     }
7751                     
7752                 ];
7753         } else if ( this.fieldLabel.length) {
7754                 Roo.log(" label");
7755                  cfg.cn = [
7756                    
7757                     {
7758                         tag: 'label',
7759                         //cls : 'input-group-addon',
7760                         html : this.fieldLabel
7761                         
7762                     },
7763                     
7764                     inputblock
7765                     
7766                 ];
7767
7768         } else {
7769             
7770                    Roo.log(" no label && no align");
7771                 cfg.cn = [
7772                     
7773                         inputblock
7774                     
7775                 ];
7776                 
7777                 
7778         }
7779         
7780         if (this.disabled) {
7781             input.disabled=true;
7782         }
7783         
7784         return cfg;
7785         
7786     },
7787     /**
7788      * return the real textarea element.
7789      */
7790     inputEl: function ()
7791     {
7792         return this.el.select('textarea.form-control',true).first();
7793     }
7794 });
7795
7796  
7797 /*
7798  * - LGPL
7799  *
7800  * trigger field - base class for combo..
7801  * 
7802  */
7803  
7804 /**
7805  * @class Roo.bootstrap.TriggerField
7806  * @extends Roo.bootstrap.Input
7807  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7808  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7809  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7810  * for which you can provide a custom implementation.  For example:
7811  * <pre><code>
7812 var trigger = new Roo.bootstrap.TriggerField();
7813 trigger.onTriggerClick = myTriggerFn;
7814 trigger.applyTo('my-field');
7815 </code></pre>
7816  *
7817  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7818  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7819  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7820  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7821  * @constructor
7822  * Create a new TriggerField.
7823  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7824  * to the base TextField)
7825  */
7826 Roo.bootstrap.TriggerField = function(config){
7827     this.mimicing = false;
7828     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7829 };
7830
7831 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7832     /**
7833      * @cfg {String} triggerClass A CSS class to apply to the trigger
7834      */
7835      /**
7836      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7837      */
7838     hideTrigger:false,
7839
7840     /** @cfg {Boolean} grow @hide */
7841     /** @cfg {Number} growMin @hide */
7842     /** @cfg {Number} growMax @hide */
7843
7844     /**
7845      * @hide 
7846      * @method
7847      */
7848     autoSize: Roo.emptyFn,
7849     // private
7850     monitorTab : true,
7851     // private
7852     deferHeight : true,
7853
7854     
7855     actionMode : 'wrap',
7856     
7857     
7858     
7859     getAutoCreate : function(){
7860        
7861         var align = this.labelAlign || this.parentLabelAlign();
7862         
7863         var id = Roo.id();
7864         
7865         var cfg = {
7866             cls: 'form-group' //input-group
7867         };
7868         
7869         
7870         var input =  {
7871             tag: 'input',
7872             id : id,
7873             type : this.inputType,
7874             cls : 'form-control',
7875             autocomplete: 'off',
7876             placeholder : this.placeholder || '' 
7877             
7878         };
7879         if (this.name) {
7880             input.name = this.name;
7881         }
7882         if (this.size) {
7883             input.cls += ' input-' + this.size;
7884         }
7885         
7886         if (this.disabled) {
7887             input.disabled=true;
7888         }
7889         
7890         var inputblock = input;
7891         
7892         if (this.before || this.after) {
7893             
7894             inputblock = {
7895                 cls : 'input-group',
7896                 cn :  [] 
7897             };
7898             if (this.before) {
7899                 inputblock.cn.push({
7900                     tag :'span',
7901                     cls : 'input-group-addon',
7902                     html : this.before
7903                 });
7904             }
7905             inputblock.cn.push(input);
7906             if (this.after) {
7907                 inputblock.cn.push({
7908                     tag :'span',
7909                     cls : 'input-group-addon',
7910                     html : this.after
7911                 });
7912             }
7913             
7914         };
7915         
7916         var box = {
7917             tag: 'div',
7918             cn: [
7919                 {
7920                     tag: 'input',
7921                     type : 'hidden',
7922                     cls: 'form-hidden-field'
7923                 },
7924                 inputblock
7925             ]
7926             
7927         };
7928         
7929         if(this.multiple){
7930             Roo.log('multiple');
7931             
7932             box = {
7933                 tag: 'div',
7934                 cn: [
7935                     {
7936                         tag: 'input',
7937                         type : 'hidden',
7938                         cls: 'form-hidden-field'
7939                     },
7940                     {
7941                         tag: 'ul',
7942                         cls: 'select2-choices',
7943                         cn:[
7944                             {
7945                                 tag: 'li',
7946                                 cls: 'select2-search-field',
7947                                 cn: [
7948
7949                                     inputblock
7950                                 ]
7951                             }
7952                         ]
7953                     }
7954                 ]
7955             }
7956         };
7957         
7958         var combobox = {
7959             cls: 'select2-container input-group',
7960             cn: [
7961                 box
7962 //                {
7963 //                    tag: 'ul',
7964 //                    cls: 'typeahead typeahead-long dropdown-menu',
7965 //                    style: 'display:none'
7966 //                }
7967             ]
7968         };
7969         
7970         if(!this.multiple && this.showToggleBtn){
7971             combobox.cn.push({
7972                 tag :'span',
7973                 cls : 'input-group-addon btn dropdown-toggle',
7974                 cn : [
7975                     {
7976                         tag: 'span',
7977                         cls: 'caret'
7978                     },
7979                     {
7980                         tag: 'span',
7981                         cls: 'combobox-clear',
7982                         cn  : [
7983                             {
7984                                 tag : 'i',
7985                                 cls: 'icon-remove'
7986                             }
7987                         ]
7988                     }
7989                 ]
7990
7991             })
7992         }
7993         
7994         if(this.multiple){
7995             combobox.cls += ' select2-container-multi';
7996         }
7997         
7998         if (align ==='left' && this.fieldLabel.length) {
7999             
8000                 Roo.log("left and has label");
8001                 cfg.cn = [
8002                     
8003                     {
8004                         tag: 'label',
8005                         'for' :  id,
8006                         cls : 'control-label col-sm-' + this.labelWidth,
8007                         html : this.fieldLabel
8008                         
8009                     },
8010                     {
8011                         cls : "col-sm-" + (12 - this.labelWidth), 
8012                         cn: [
8013                             combobox
8014                         ]
8015                     }
8016                     
8017                 ];
8018         } else if ( this.fieldLabel.length) {
8019                 Roo.log(" label");
8020                  cfg.cn = [
8021                    
8022                     {
8023                         tag: 'label',
8024                         //cls : 'input-group-addon',
8025                         html : this.fieldLabel
8026                         
8027                     },
8028                     
8029                     combobox
8030                     
8031                 ];
8032
8033         } else {
8034             
8035                 Roo.log(" no label && no align");
8036                 cfg = combobox
8037                      
8038                 
8039         }
8040          
8041         var settings=this;
8042         ['xs','sm','md','lg'].map(function(size){
8043             if (settings[size]) {
8044                 cfg.cls += ' col-' + size + '-' + settings[size];
8045             }
8046         });
8047         
8048         return cfg;
8049         
8050     },
8051     
8052     
8053     
8054     // private
8055     onResize : function(w, h){
8056 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8057 //        if(typeof w == 'number'){
8058 //            var x = w - this.trigger.getWidth();
8059 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8060 //            this.trigger.setStyle('left', x+'px');
8061 //        }
8062     },
8063
8064     // private
8065     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8066
8067     // private
8068     getResizeEl : function(){
8069         return this.inputEl();
8070     },
8071
8072     // private
8073     getPositionEl : function(){
8074         return this.inputEl();
8075     },
8076
8077     // private
8078     alignErrorIcon : function(){
8079         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8080     },
8081
8082     // private
8083     initEvents : function(){
8084         
8085         this.createList();
8086         
8087         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8088         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8089         if(!this.multiple && this.showToggleBtn){
8090             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8091             if(this.hideTrigger){
8092                 this.trigger.setDisplayed(false);
8093             }
8094             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8095         }
8096         
8097         if(this.multiple){
8098             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8099         }
8100         
8101         //this.trigger.addClassOnOver('x-form-trigger-over');
8102         //this.trigger.addClassOnClick('x-form-trigger-click');
8103         
8104         //if(!this.width){
8105         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8106         //}
8107     },
8108     
8109     createList : function()
8110     {
8111         this.list = Roo.get(document.body).createChild({
8112             tag: 'ul',
8113             cls: 'typeahead typeahead-long dropdown-menu',
8114             style: 'display:none'
8115         });
8116         
8117         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8118         
8119     },
8120
8121     // private
8122     initTrigger : function(){
8123        
8124     },
8125
8126     // private
8127     onDestroy : function(){
8128         if(this.trigger){
8129             this.trigger.removeAllListeners();
8130           //  this.trigger.remove();
8131         }
8132         //if(this.wrap){
8133         //    this.wrap.remove();
8134         //}
8135         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8136     },
8137
8138     // private
8139     onFocus : function(){
8140         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8141         /*
8142         if(!this.mimicing){
8143             this.wrap.addClass('x-trigger-wrap-focus');
8144             this.mimicing = true;
8145             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8146             if(this.monitorTab){
8147                 this.el.on("keydown", this.checkTab, this);
8148             }
8149         }
8150         */
8151     },
8152
8153     // private
8154     checkTab : function(e){
8155         if(e.getKey() == e.TAB){
8156             this.triggerBlur();
8157         }
8158     },
8159
8160     // private
8161     onBlur : function(){
8162         // do nothing
8163     },
8164
8165     // private
8166     mimicBlur : function(e, t){
8167         /*
8168         if(!this.wrap.contains(t) && this.validateBlur()){
8169             this.triggerBlur();
8170         }
8171         */
8172     },
8173
8174     // private
8175     triggerBlur : function(){
8176         this.mimicing = false;
8177         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8178         if(this.monitorTab){
8179             this.el.un("keydown", this.checkTab, this);
8180         }
8181         //this.wrap.removeClass('x-trigger-wrap-focus');
8182         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8183     },
8184
8185     // private
8186     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8187     validateBlur : function(e, t){
8188         return true;
8189     },
8190
8191     // private
8192     onDisable : function(){
8193         this.inputEl().dom.disabled = true;
8194         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8195         //if(this.wrap){
8196         //    this.wrap.addClass('x-item-disabled');
8197         //}
8198     },
8199
8200     // private
8201     onEnable : function(){
8202         this.inputEl().dom.disabled = false;
8203         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8204         //if(this.wrap){
8205         //    this.el.removeClass('x-item-disabled');
8206         //}
8207     },
8208
8209     // private
8210     onShow : function(){
8211         var ae = this.getActionEl();
8212         
8213         if(ae){
8214             ae.dom.style.display = '';
8215             ae.dom.style.visibility = 'visible';
8216         }
8217     },
8218
8219     // private
8220     
8221     onHide : function(){
8222         var ae = this.getActionEl();
8223         ae.dom.style.display = 'none';
8224     },
8225
8226     /**
8227      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8228      * by an implementing function.
8229      * @method
8230      * @param {EventObject} e
8231      */
8232     onTriggerClick : Roo.emptyFn
8233 });
8234  /*
8235  * Based on:
8236  * Ext JS Library 1.1.1
8237  * Copyright(c) 2006-2007, Ext JS, LLC.
8238  *
8239  * Originally Released Under LGPL - original licence link has changed is not relivant.
8240  *
8241  * Fork - LGPL
8242  * <script type="text/javascript">
8243  */
8244
8245
8246 /**
8247  * @class Roo.data.SortTypes
8248  * @singleton
8249  * Defines the default sorting (casting?) comparison functions used when sorting data.
8250  */
8251 Roo.data.SortTypes = {
8252     /**
8253      * Default sort that does nothing
8254      * @param {Mixed} s The value being converted
8255      * @return {Mixed} The comparison value
8256      */
8257     none : function(s){
8258         return s;
8259     },
8260     
8261     /**
8262      * The regular expression used to strip tags
8263      * @type {RegExp}
8264      * @property
8265      */
8266     stripTagsRE : /<\/?[^>]+>/gi,
8267     
8268     /**
8269      * Strips all HTML tags to sort on text only
8270      * @param {Mixed} s The value being converted
8271      * @return {String} The comparison value
8272      */
8273     asText : function(s){
8274         return String(s).replace(this.stripTagsRE, "");
8275     },
8276     
8277     /**
8278      * Strips all HTML tags to sort on text only - Case insensitive
8279      * @param {Mixed} s The value being converted
8280      * @return {String} The comparison value
8281      */
8282     asUCText : function(s){
8283         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8284     },
8285     
8286     /**
8287      * Case insensitive string
8288      * @param {Mixed} s The value being converted
8289      * @return {String} The comparison value
8290      */
8291     asUCString : function(s) {
8292         return String(s).toUpperCase();
8293     },
8294     
8295     /**
8296      * Date sorting
8297      * @param {Mixed} s The value being converted
8298      * @return {Number} The comparison value
8299      */
8300     asDate : function(s) {
8301         if(!s){
8302             return 0;
8303         }
8304         if(s instanceof Date){
8305             return s.getTime();
8306         }
8307         return Date.parse(String(s));
8308     },
8309     
8310     /**
8311      * Float sorting
8312      * @param {Mixed} s The value being converted
8313      * @return {Float} The comparison value
8314      */
8315     asFloat : function(s) {
8316         var val = parseFloat(String(s).replace(/,/g, ""));
8317         if(isNaN(val)) val = 0;
8318         return val;
8319     },
8320     
8321     /**
8322      * Integer sorting
8323      * @param {Mixed} s The value being converted
8324      * @return {Number} The comparison value
8325      */
8326     asInt : function(s) {
8327         var val = parseInt(String(s).replace(/,/g, ""));
8328         if(isNaN(val)) val = 0;
8329         return val;
8330     }
8331 };/*
8332  * Based on:
8333  * Ext JS Library 1.1.1
8334  * Copyright(c) 2006-2007, Ext JS, LLC.
8335  *
8336  * Originally Released Under LGPL - original licence link has changed is not relivant.
8337  *
8338  * Fork - LGPL
8339  * <script type="text/javascript">
8340  */
8341
8342 /**
8343 * @class Roo.data.Record
8344  * Instances of this class encapsulate both record <em>definition</em> information, and record
8345  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8346  * to access Records cached in an {@link Roo.data.Store} object.<br>
8347  * <p>
8348  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8349  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8350  * objects.<br>
8351  * <p>
8352  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8353  * @constructor
8354  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8355  * {@link #create}. The parameters are the same.
8356  * @param {Array} data An associative Array of data values keyed by the field name.
8357  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8358  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8359  * not specified an integer id is generated.
8360  */
8361 Roo.data.Record = function(data, id){
8362     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8363     this.data = data;
8364 };
8365
8366 /**
8367  * Generate a constructor for a specific record layout.
8368  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8369  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8370  * Each field definition object may contain the following properties: <ul>
8371  * <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,
8372  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8373  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8374  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8375  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8376  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8377  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8378  * this may be omitted.</p></li>
8379  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8380  * <ul><li>auto (Default, implies no conversion)</li>
8381  * <li>string</li>
8382  * <li>int</li>
8383  * <li>float</li>
8384  * <li>boolean</li>
8385  * <li>date</li></ul></p></li>
8386  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8387  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8388  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8389  * by the Reader into an object that will be stored in the Record. It is passed the
8390  * following parameters:<ul>
8391  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8392  * </ul></p></li>
8393  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8394  * </ul>
8395  * <br>usage:<br><pre><code>
8396 var TopicRecord = Roo.data.Record.create(
8397     {name: 'title', mapping: 'topic_title'},
8398     {name: 'author', mapping: 'username'},
8399     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8400     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8401     {name: 'lastPoster', mapping: 'user2'},
8402     {name: 'excerpt', mapping: 'post_text'}
8403 );
8404
8405 var myNewRecord = new TopicRecord({
8406     title: 'Do my job please',
8407     author: 'noobie',
8408     totalPosts: 1,
8409     lastPost: new Date(),
8410     lastPoster: 'Animal',
8411     excerpt: 'No way dude!'
8412 });
8413 myStore.add(myNewRecord);
8414 </code></pre>
8415  * @method create
8416  * @static
8417  */
8418 Roo.data.Record.create = function(o){
8419     var f = function(){
8420         f.superclass.constructor.apply(this, arguments);
8421     };
8422     Roo.extend(f, Roo.data.Record);
8423     var p = f.prototype;
8424     p.fields = new Roo.util.MixedCollection(false, function(field){
8425         return field.name;
8426     });
8427     for(var i = 0, len = o.length; i < len; i++){
8428         p.fields.add(new Roo.data.Field(o[i]));
8429     }
8430     f.getField = function(name){
8431         return p.fields.get(name);  
8432     };
8433     return f;
8434 };
8435
8436 Roo.data.Record.AUTO_ID = 1000;
8437 Roo.data.Record.EDIT = 'edit';
8438 Roo.data.Record.REJECT = 'reject';
8439 Roo.data.Record.COMMIT = 'commit';
8440
8441 Roo.data.Record.prototype = {
8442     /**
8443      * Readonly flag - true if this record has been modified.
8444      * @type Boolean
8445      */
8446     dirty : false,
8447     editing : false,
8448     error: null,
8449     modified: null,
8450
8451     // private
8452     join : function(store){
8453         this.store = store;
8454     },
8455
8456     /**
8457      * Set the named field to the specified value.
8458      * @param {String} name The name of the field to set.
8459      * @param {Object} value The value to set the field to.
8460      */
8461     set : function(name, value){
8462         if(this.data[name] == value){
8463             return;
8464         }
8465         this.dirty = true;
8466         if(!this.modified){
8467             this.modified = {};
8468         }
8469         if(typeof this.modified[name] == 'undefined'){
8470             this.modified[name] = this.data[name];
8471         }
8472         this.data[name] = value;
8473         if(!this.editing && this.store){
8474             this.store.afterEdit(this);
8475         }       
8476     },
8477
8478     /**
8479      * Get the value of the named field.
8480      * @param {String} name The name of the field to get the value of.
8481      * @return {Object} The value of the field.
8482      */
8483     get : function(name){
8484         return this.data[name]; 
8485     },
8486
8487     // private
8488     beginEdit : function(){
8489         this.editing = true;
8490         this.modified = {}; 
8491     },
8492
8493     // private
8494     cancelEdit : function(){
8495         this.editing = false;
8496         delete this.modified;
8497     },
8498
8499     // private
8500     endEdit : function(){
8501         this.editing = false;
8502         if(this.dirty && this.store){
8503             this.store.afterEdit(this);
8504         }
8505     },
8506
8507     /**
8508      * Usually called by the {@link Roo.data.Store} which owns the Record.
8509      * Rejects all changes made to the Record since either creation, or the last commit operation.
8510      * Modified fields are reverted to their original values.
8511      * <p>
8512      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8513      * of reject operations.
8514      */
8515     reject : function(){
8516         var m = this.modified;
8517         for(var n in m){
8518             if(typeof m[n] != "function"){
8519                 this.data[n] = m[n];
8520             }
8521         }
8522         this.dirty = false;
8523         delete this.modified;
8524         this.editing = false;
8525         if(this.store){
8526             this.store.afterReject(this);
8527         }
8528     },
8529
8530     /**
8531      * Usually called by the {@link Roo.data.Store} which owns the Record.
8532      * Commits all changes made to the Record since either creation, or the last commit operation.
8533      * <p>
8534      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8535      * of commit operations.
8536      */
8537     commit : function(){
8538         this.dirty = false;
8539         delete this.modified;
8540         this.editing = false;
8541         if(this.store){
8542             this.store.afterCommit(this);
8543         }
8544     },
8545
8546     // private
8547     hasError : function(){
8548         return this.error != null;
8549     },
8550
8551     // private
8552     clearError : function(){
8553         this.error = null;
8554     },
8555
8556     /**
8557      * Creates a copy of this record.
8558      * @param {String} id (optional) A new record id if you don't want to use this record's id
8559      * @return {Record}
8560      */
8561     copy : function(newId) {
8562         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8563     }
8564 };/*
8565  * Based on:
8566  * Ext JS Library 1.1.1
8567  * Copyright(c) 2006-2007, Ext JS, LLC.
8568  *
8569  * Originally Released Under LGPL - original licence link has changed is not relivant.
8570  *
8571  * Fork - LGPL
8572  * <script type="text/javascript">
8573  */
8574
8575
8576
8577 /**
8578  * @class Roo.data.Store
8579  * @extends Roo.util.Observable
8580  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8581  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8582  * <p>
8583  * 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
8584  * has no knowledge of the format of the data returned by the Proxy.<br>
8585  * <p>
8586  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8587  * instances from the data object. These records are cached and made available through accessor functions.
8588  * @constructor
8589  * Creates a new Store.
8590  * @param {Object} config A config object containing the objects needed for the Store to access data,
8591  * and read the data into Records.
8592  */
8593 Roo.data.Store = function(config){
8594     this.data = new Roo.util.MixedCollection(false);
8595     this.data.getKey = function(o){
8596         return o.id;
8597     };
8598     this.baseParams = {};
8599     // private
8600     this.paramNames = {
8601         "start" : "start",
8602         "limit" : "limit",
8603         "sort" : "sort",
8604         "dir" : "dir",
8605         "multisort" : "_multisort"
8606     };
8607
8608     if(config && config.data){
8609         this.inlineData = config.data;
8610         delete config.data;
8611     }
8612
8613     Roo.apply(this, config);
8614     
8615     if(this.reader){ // reader passed
8616         this.reader = Roo.factory(this.reader, Roo.data);
8617         this.reader.xmodule = this.xmodule || false;
8618         if(!this.recordType){
8619             this.recordType = this.reader.recordType;
8620         }
8621         if(this.reader.onMetaChange){
8622             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8623         }
8624     }
8625
8626     if(this.recordType){
8627         this.fields = this.recordType.prototype.fields;
8628     }
8629     this.modified = [];
8630
8631     this.addEvents({
8632         /**
8633          * @event datachanged
8634          * Fires when the data cache has changed, and a widget which is using this Store
8635          * as a Record cache should refresh its view.
8636          * @param {Store} this
8637          */
8638         datachanged : true,
8639         /**
8640          * @event metachange
8641          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8642          * @param {Store} this
8643          * @param {Object} meta The JSON metadata
8644          */
8645         metachange : true,
8646         /**
8647          * @event add
8648          * Fires when Records have been added to the Store
8649          * @param {Store} this
8650          * @param {Roo.data.Record[]} records The array of Records added
8651          * @param {Number} index The index at which the record(s) were added
8652          */
8653         add : true,
8654         /**
8655          * @event remove
8656          * Fires when a Record has been removed from the Store
8657          * @param {Store} this
8658          * @param {Roo.data.Record} record The Record that was removed
8659          * @param {Number} index The index at which the record was removed
8660          */
8661         remove : true,
8662         /**
8663          * @event update
8664          * Fires when a Record has been updated
8665          * @param {Store} this
8666          * @param {Roo.data.Record} record The Record that was updated
8667          * @param {String} operation The update operation being performed.  Value may be one of:
8668          * <pre><code>
8669  Roo.data.Record.EDIT
8670  Roo.data.Record.REJECT
8671  Roo.data.Record.COMMIT
8672          * </code></pre>
8673          */
8674         update : true,
8675         /**
8676          * @event clear
8677          * Fires when the data cache has been cleared.
8678          * @param {Store} this
8679          */
8680         clear : true,
8681         /**
8682          * @event beforeload
8683          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8684          * the load action will be canceled.
8685          * @param {Store} this
8686          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8687          */
8688         beforeload : true,
8689         /**
8690          * @event beforeloadadd
8691          * Fires after a new set of Records has been loaded.
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          */
8696         beforeloadadd : true,
8697         /**
8698          * @event load
8699          * Fires after a new set of Records has been loaded, before they are added to the store.
8700          * @param {Store} this
8701          * @param {Roo.data.Record[]} records The Records that were loaded
8702          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8703          * @params {Object} return from reader
8704          */
8705         load : true,
8706         /**
8707          * @event loadexception
8708          * Fires if an exception occurs in the Proxy during loading.
8709          * Called with the signature of the Proxy's "loadexception" event.
8710          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8711          * 
8712          * @param {Proxy} 
8713          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8714          * @param {Object} load options 
8715          * @param {Object} jsonData from your request (normally this contains the Exception)
8716          */
8717         loadexception : true
8718     });
8719     
8720     if(this.proxy){
8721         this.proxy = Roo.factory(this.proxy, Roo.data);
8722         this.proxy.xmodule = this.xmodule || false;
8723         this.relayEvents(this.proxy,  ["loadexception"]);
8724     }
8725     this.sortToggle = {};
8726     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8727
8728     Roo.data.Store.superclass.constructor.call(this);
8729
8730     if(this.inlineData){
8731         this.loadData(this.inlineData);
8732         delete this.inlineData;
8733     }
8734 };
8735
8736 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8737      /**
8738     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8739     * without a remote query - used by combo/forms at present.
8740     */
8741     
8742     /**
8743     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8744     */
8745     /**
8746     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8747     */
8748     /**
8749     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8750     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8751     */
8752     /**
8753     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8754     * on any HTTP request
8755     */
8756     /**
8757     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8758     */
8759     /**
8760     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8761     */
8762     multiSort: false,
8763     /**
8764     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8765     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8766     */
8767     remoteSort : false,
8768
8769     /**
8770     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8771      * loaded or when a record is removed. (defaults to false).
8772     */
8773     pruneModifiedRecords : false,
8774
8775     // private
8776     lastOptions : null,
8777
8778     /**
8779      * Add Records to the Store and fires the add event.
8780      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8781      */
8782     add : function(records){
8783         records = [].concat(records);
8784         for(var i = 0, len = records.length; i < len; i++){
8785             records[i].join(this);
8786         }
8787         var index = this.data.length;
8788         this.data.addAll(records);
8789         this.fireEvent("add", this, records, index);
8790     },
8791
8792     /**
8793      * Remove a Record from the Store and fires the remove event.
8794      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8795      */
8796     remove : function(record){
8797         var index = this.data.indexOf(record);
8798         this.data.removeAt(index);
8799         if(this.pruneModifiedRecords){
8800             this.modified.remove(record);
8801         }
8802         this.fireEvent("remove", this, record, index);
8803     },
8804
8805     /**
8806      * Remove all Records from the Store and fires the clear event.
8807      */
8808     removeAll : function(){
8809         this.data.clear();
8810         if(this.pruneModifiedRecords){
8811             this.modified = [];
8812         }
8813         this.fireEvent("clear", this);
8814     },
8815
8816     /**
8817      * Inserts Records to the Store at the given index and fires the add event.
8818      * @param {Number} index The start index at which to insert the passed Records.
8819      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8820      */
8821     insert : function(index, records){
8822         records = [].concat(records);
8823         for(var i = 0, len = records.length; i < len; i++){
8824             this.data.insert(index, records[i]);
8825             records[i].join(this);
8826         }
8827         this.fireEvent("add", this, records, index);
8828     },
8829
8830     /**
8831      * Get the index within the cache of the passed Record.
8832      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8833      * @return {Number} The index of the passed Record. Returns -1 if not found.
8834      */
8835     indexOf : function(record){
8836         return this.data.indexOf(record);
8837     },
8838
8839     /**
8840      * Get the index within the cache of the Record with the passed id.
8841      * @param {String} id The id of the Record to find.
8842      * @return {Number} The index of the Record. Returns -1 if not found.
8843      */
8844     indexOfId : function(id){
8845         return this.data.indexOfKey(id);
8846     },
8847
8848     /**
8849      * Get the Record with the specified id.
8850      * @param {String} id The id of the Record to find.
8851      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8852      */
8853     getById : function(id){
8854         return this.data.key(id);
8855     },
8856
8857     /**
8858      * Get the Record at the specified index.
8859      * @param {Number} index The index of the Record to find.
8860      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8861      */
8862     getAt : function(index){
8863         return this.data.itemAt(index);
8864     },
8865
8866     /**
8867      * Returns a range of Records between specified indices.
8868      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8869      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8870      * @return {Roo.data.Record[]} An array of Records
8871      */
8872     getRange : function(start, end){
8873         return this.data.getRange(start, end);
8874     },
8875
8876     // private
8877     storeOptions : function(o){
8878         o = Roo.apply({}, o);
8879         delete o.callback;
8880         delete o.scope;
8881         this.lastOptions = o;
8882     },
8883
8884     /**
8885      * Loads the Record cache from the configured Proxy using the configured Reader.
8886      * <p>
8887      * If using remote paging, then the first load call must specify the <em>start</em>
8888      * and <em>limit</em> properties in the options.params property to establish the initial
8889      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8890      * <p>
8891      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8892      * and this call will return before the new data has been loaded. Perform any post-processing
8893      * in a callback function, or in a "load" event handler.</strong>
8894      * <p>
8895      * @param {Object} options An object containing properties which control loading options:<ul>
8896      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8897      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8898      * passed the following arguments:<ul>
8899      * <li>r : Roo.data.Record[]</li>
8900      * <li>options: Options object from the load call</li>
8901      * <li>success: Boolean success indicator</li></ul></li>
8902      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8903      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8904      * </ul>
8905      */
8906     load : function(options){
8907         options = options || {};
8908         if(this.fireEvent("beforeload", this, options) !== false){
8909             this.storeOptions(options);
8910             var p = Roo.apply(options.params || {}, this.baseParams);
8911             // if meta was not loaded from remote source.. try requesting it.
8912             if (!this.reader.metaFromRemote) {
8913                 p._requestMeta = 1;
8914             }
8915             if(this.sortInfo && this.remoteSort){
8916                 var pn = this.paramNames;
8917                 p[pn["sort"]] = this.sortInfo.field;
8918                 p[pn["dir"]] = this.sortInfo.direction;
8919             }
8920             if (this.multiSort) {
8921                 var pn = this.paramNames;
8922                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8923             }
8924             
8925             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8926         }
8927     },
8928
8929     /**
8930      * Reloads the Record cache from the configured Proxy using the configured Reader and
8931      * the options from the last load operation performed.
8932      * @param {Object} options (optional) An object containing properties which may override the options
8933      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8934      * the most recently used options are reused).
8935      */
8936     reload : function(options){
8937         this.load(Roo.applyIf(options||{}, this.lastOptions));
8938     },
8939
8940     // private
8941     // Called as a callback by the Reader during a load operation.
8942     loadRecords : function(o, options, success){
8943         if(!o || success === false){
8944             if(success !== false){
8945                 this.fireEvent("load", this, [], options, o);
8946             }
8947             if(options.callback){
8948                 options.callback.call(options.scope || this, [], options, false);
8949             }
8950             return;
8951         }
8952         // if data returned failure - throw an exception.
8953         if (o.success === false) {
8954             // show a message if no listener is registered.
8955             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8956                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8957             }
8958             // loadmask wil be hooked into this..
8959             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8960             return;
8961         }
8962         var r = o.records, t = o.totalRecords || r.length;
8963         
8964         this.fireEvent("beforeloadadd", this, r, options, o);
8965         
8966         if(!options || options.add !== true){
8967             if(this.pruneModifiedRecords){
8968                 this.modified = [];
8969             }
8970             for(var i = 0, len = r.length; i < len; i++){
8971                 r[i].join(this);
8972             }
8973             if(this.snapshot){
8974                 this.data = this.snapshot;
8975                 delete this.snapshot;
8976             }
8977             this.data.clear();
8978             this.data.addAll(r);
8979             this.totalLength = t;
8980             this.applySort();
8981             this.fireEvent("datachanged", this);
8982         }else{
8983             this.totalLength = Math.max(t, this.data.length+r.length);
8984             this.add(r);
8985         }
8986         this.fireEvent("load", this, r, options, o);
8987         if(options.callback){
8988             options.callback.call(options.scope || this, r, options, true);
8989         }
8990     },
8991
8992
8993     /**
8994      * Loads data from a passed data block. A Reader which understands the format of the data
8995      * must have been configured in the constructor.
8996      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8997      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8998      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8999      */
9000     loadData : function(o, append){
9001         var r = this.reader.readRecords(o);
9002         this.loadRecords(r, {add: append}, true);
9003     },
9004
9005     /**
9006      * Gets the number of cached records.
9007      * <p>
9008      * <em>If using paging, this may not be the total size of the dataset. If the data object
9009      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9010      * the data set size</em>
9011      */
9012     getCount : function(){
9013         return this.data.length || 0;
9014     },
9015
9016     /**
9017      * Gets the total number of records in the dataset as returned by the server.
9018      * <p>
9019      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9020      * the dataset size</em>
9021      */
9022     getTotalCount : function(){
9023         return this.totalLength || 0;
9024     },
9025
9026     /**
9027      * Returns the sort state of the Store as an object with two properties:
9028      * <pre><code>
9029  field {String} The name of the field by which the Records are sorted
9030  direction {String} The sort order, "ASC" or "DESC"
9031      * </code></pre>
9032      */
9033     getSortState : function(){
9034         return this.sortInfo;
9035     },
9036
9037     // private
9038     applySort : function(){
9039         if(this.sortInfo && !this.remoteSort){
9040             var s = this.sortInfo, f = s.field;
9041             var st = this.fields.get(f).sortType;
9042             var fn = function(r1, r2){
9043                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9044                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9045             };
9046             this.data.sort(s.direction, fn);
9047             if(this.snapshot && this.snapshot != this.data){
9048                 this.snapshot.sort(s.direction, fn);
9049             }
9050         }
9051     },
9052
9053     /**
9054      * Sets the default sort column and order to be used by the next load operation.
9055      * @param {String} fieldName The name of the field to sort by.
9056      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9057      */
9058     setDefaultSort : function(field, dir){
9059         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9060     },
9061
9062     /**
9063      * Sort the Records.
9064      * If remote sorting is used, the sort is performed on the server, and the cache is
9065      * reloaded. If local sorting is used, the cache is sorted internally.
9066      * @param {String} fieldName The name of the field to sort by.
9067      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9068      */
9069     sort : function(fieldName, dir){
9070         var f = this.fields.get(fieldName);
9071         if(!dir){
9072             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9073             
9074             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9075                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9076             }else{
9077                 dir = f.sortDir;
9078             }
9079         }
9080         this.sortToggle[f.name] = dir;
9081         this.sortInfo = {field: f.name, direction: dir};
9082         if(!this.remoteSort){
9083             this.applySort();
9084             this.fireEvent("datachanged", this);
9085         }else{
9086             this.load(this.lastOptions);
9087         }
9088     },
9089
9090     /**
9091      * Calls the specified function for each of the Records in the cache.
9092      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9093      * Returning <em>false</em> aborts and exits the iteration.
9094      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9095      */
9096     each : function(fn, scope){
9097         this.data.each(fn, scope);
9098     },
9099
9100     /**
9101      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9102      * (e.g., during paging).
9103      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9104      */
9105     getModifiedRecords : function(){
9106         return this.modified;
9107     },
9108
9109     // private
9110     createFilterFn : function(property, value, anyMatch){
9111         if(!value.exec){ // not a regex
9112             value = String(value);
9113             if(value.length == 0){
9114                 return false;
9115             }
9116             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9117         }
9118         return function(r){
9119             return value.test(r.data[property]);
9120         };
9121     },
9122
9123     /**
9124      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9125      * @param {String} property A field on your records
9126      * @param {Number} start The record index to start at (defaults to 0)
9127      * @param {Number} end The last record index to include (defaults to length - 1)
9128      * @return {Number} The sum
9129      */
9130     sum : function(property, start, end){
9131         var rs = this.data.items, v = 0;
9132         start = start || 0;
9133         end = (end || end === 0) ? end : rs.length-1;
9134
9135         for(var i = start; i <= end; i++){
9136             v += (rs[i].data[property] || 0);
9137         }
9138         return v;
9139     },
9140
9141     /**
9142      * Filter the records by a specified property.
9143      * @param {String} field A field on your records
9144      * @param {String/RegExp} value Either a string that the field
9145      * should start with or a RegExp to test against the field
9146      * @param {Boolean} anyMatch True to match any part not just the beginning
9147      */
9148     filter : function(property, value, anyMatch){
9149         var fn = this.createFilterFn(property, value, anyMatch);
9150         return fn ? this.filterBy(fn) : this.clearFilter();
9151     },
9152
9153     /**
9154      * Filter by a function. The specified function will be called with each
9155      * record in this data source. If the function returns true the record is included,
9156      * otherwise it is filtered.
9157      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9158      * @param {Object} scope (optional) The scope of the function (defaults to this)
9159      */
9160     filterBy : function(fn, scope){
9161         this.snapshot = this.snapshot || this.data;
9162         this.data = this.queryBy(fn, scope||this);
9163         this.fireEvent("datachanged", this);
9164     },
9165
9166     /**
9167      * Query the records by a specified property.
9168      * @param {String} field A field on your records
9169      * @param {String/RegExp} value Either a string that the field
9170      * should start with or a RegExp to test against the field
9171      * @param {Boolean} anyMatch True to match any part not just the beginning
9172      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9173      */
9174     query : function(property, value, anyMatch){
9175         var fn = this.createFilterFn(property, value, anyMatch);
9176         return fn ? this.queryBy(fn) : this.data.clone();
9177     },
9178
9179     /**
9180      * Query by a function. The specified function will be called with each
9181      * record in this data source. If the function returns true the record is included
9182      * in the results.
9183      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9184      * @param {Object} scope (optional) The scope of the function (defaults to this)
9185       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9186      **/
9187     queryBy : function(fn, scope){
9188         var data = this.snapshot || this.data;
9189         return data.filterBy(fn, scope||this);
9190     },
9191
9192     /**
9193      * Collects unique values for a particular dataIndex from this store.
9194      * @param {String} dataIndex The property to collect
9195      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9196      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9197      * @return {Array} An array of the unique values
9198      **/
9199     collect : function(dataIndex, allowNull, bypassFilter){
9200         var d = (bypassFilter === true && this.snapshot) ?
9201                 this.snapshot.items : this.data.items;
9202         var v, sv, r = [], l = {};
9203         for(var i = 0, len = d.length; i < len; i++){
9204             v = d[i].data[dataIndex];
9205             sv = String(v);
9206             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9207                 l[sv] = true;
9208                 r[r.length] = v;
9209             }
9210         }
9211         return r;
9212     },
9213
9214     /**
9215      * Revert to a view of the Record cache with no filtering applied.
9216      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9217      */
9218     clearFilter : function(suppressEvent){
9219         if(this.snapshot && this.snapshot != this.data){
9220             this.data = this.snapshot;
9221             delete this.snapshot;
9222             if(suppressEvent !== true){
9223                 this.fireEvent("datachanged", this);
9224             }
9225         }
9226     },
9227
9228     // private
9229     afterEdit : function(record){
9230         if(this.modified.indexOf(record) == -1){
9231             this.modified.push(record);
9232         }
9233         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9234     },
9235     
9236     // private
9237     afterReject : function(record){
9238         this.modified.remove(record);
9239         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9240     },
9241
9242     // private
9243     afterCommit : function(record){
9244         this.modified.remove(record);
9245         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9246     },
9247
9248     /**
9249      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9250      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9251      */
9252     commitChanges : function(){
9253         var m = this.modified.slice(0);
9254         this.modified = [];
9255         for(var i = 0, len = m.length; i < len; i++){
9256             m[i].commit();
9257         }
9258     },
9259
9260     /**
9261      * Cancel outstanding changes on all changed records.
9262      */
9263     rejectChanges : function(){
9264         var m = this.modified.slice(0);
9265         this.modified = [];
9266         for(var i = 0, len = m.length; i < len; i++){
9267             m[i].reject();
9268         }
9269     },
9270
9271     onMetaChange : function(meta, rtype, o){
9272         this.recordType = rtype;
9273         this.fields = rtype.prototype.fields;
9274         delete this.snapshot;
9275         this.sortInfo = meta.sortInfo || this.sortInfo;
9276         this.modified = [];
9277         this.fireEvent('metachange', this, this.reader.meta);
9278     },
9279     
9280     moveIndex : function(data, type)
9281     {
9282         var index = this.indexOf(data);
9283         
9284         var newIndex = index + type;
9285         
9286         this.remove(data);
9287         
9288         this.insert(newIndex, data);
9289         
9290     }
9291 });/*
9292  * Based on:
9293  * Ext JS Library 1.1.1
9294  * Copyright(c) 2006-2007, Ext JS, LLC.
9295  *
9296  * Originally Released Under LGPL - original licence link has changed is not relivant.
9297  *
9298  * Fork - LGPL
9299  * <script type="text/javascript">
9300  */
9301
9302 /**
9303  * @class Roo.data.SimpleStore
9304  * @extends Roo.data.Store
9305  * Small helper class to make creating Stores from Array data easier.
9306  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9307  * @cfg {Array} fields An array of field definition objects, or field name strings.
9308  * @cfg {Array} data The multi-dimensional array of data
9309  * @constructor
9310  * @param {Object} config
9311  */
9312 Roo.data.SimpleStore = function(config){
9313     Roo.data.SimpleStore.superclass.constructor.call(this, {
9314         isLocal : true,
9315         reader: new Roo.data.ArrayReader({
9316                 id: config.id
9317             },
9318             Roo.data.Record.create(config.fields)
9319         ),
9320         proxy : new Roo.data.MemoryProxy(config.data)
9321     });
9322     this.load();
9323 };
9324 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9325  * Based on:
9326  * Ext JS Library 1.1.1
9327  * Copyright(c) 2006-2007, Ext JS, LLC.
9328  *
9329  * Originally Released Under LGPL - original licence link has changed is not relivant.
9330  *
9331  * Fork - LGPL
9332  * <script type="text/javascript">
9333  */
9334
9335 /**
9336 /**
9337  * @extends Roo.data.Store
9338  * @class Roo.data.JsonStore
9339  * Small helper class to make creating Stores for JSON data easier. <br/>
9340 <pre><code>
9341 var store = new Roo.data.JsonStore({
9342     url: 'get-images.php',
9343     root: 'images',
9344     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9345 });
9346 </code></pre>
9347  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9348  * JsonReader and HttpProxy (unless inline data is provided).</b>
9349  * @cfg {Array} fields An array of field definition objects, or field name strings.
9350  * @constructor
9351  * @param {Object} config
9352  */
9353 Roo.data.JsonStore = function(c){
9354     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9355         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9356         reader: new Roo.data.JsonReader(c, c.fields)
9357     }));
9358 };
9359 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9360  * Based on:
9361  * Ext JS Library 1.1.1
9362  * Copyright(c) 2006-2007, Ext JS, LLC.
9363  *
9364  * Originally Released Under LGPL - original licence link has changed is not relivant.
9365  *
9366  * Fork - LGPL
9367  * <script type="text/javascript">
9368  */
9369
9370  
9371 Roo.data.Field = function(config){
9372     if(typeof config == "string"){
9373         config = {name: config};
9374     }
9375     Roo.apply(this, config);
9376     
9377     if(!this.type){
9378         this.type = "auto";
9379     }
9380     
9381     var st = Roo.data.SortTypes;
9382     // named sortTypes are supported, here we look them up
9383     if(typeof this.sortType == "string"){
9384         this.sortType = st[this.sortType];
9385     }
9386     
9387     // set default sortType for strings and dates
9388     if(!this.sortType){
9389         switch(this.type){
9390             case "string":
9391                 this.sortType = st.asUCString;
9392                 break;
9393             case "date":
9394                 this.sortType = st.asDate;
9395                 break;
9396             default:
9397                 this.sortType = st.none;
9398         }
9399     }
9400
9401     // define once
9402     var stripRe = /[\$,%]/g;
9403
9404     // prebuilt conversion function for this field, instead of
9405     // switching every time we're reading a value
9406     if(!this.convert){
9407         var cv, dateFormat = this.dateFormat;
9408         switch(this.type){
9409             case "":
9410             case "auto":
9411             case undefined:
9412                 cv = function(v){ return v; };
9413                 break;
9414             case "string":
9415                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9416                 break;
9417             case "int":
9418                 cv = function(v){
9419                     return v !== undefined && v !== null && v !== '' ?
9420                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9421                     };
9422                 break;
9423             case "float":
9424                 cv = function(v){
9425                     return v !== undefined && v !== null && v !== '' ?
9426                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9427                     };
9428                 break;
9429             case "bool":
9430             case "boolean":
9431                 cv = function(v){ return v === true || v === "true" || v == 1; };
9432                 break;
9433             case "date":
9434                 cv = function(v){
9435                     if(!v){
9436                         return '';
9437                     }
9438                     if(v instanceof Date){
9439                         return v;
9440                     }
9441                     if(dateFormat){
9442                         if(dateFormat == "timestamp"){
9443                             return new Date(v*1000);
9444                         }
9445                         return Date.parseDate(v, dateFormat);
9446                     }
9447                     var parsed = Date.parse(v);
9448                     return parsed ? new Date(parsed) : null;
9449                 };
9450              break;
9451             
9452         }
9453         this.convert = cv;
9454     }
9455 };
9456
9457 Roo.data.Field.prototype = {
9458     dateFormat: null,
9459     defaultValue: "",
9460     mapping: null,
9461     sortType : null,
9462     sortDir : "ASC"
9463 };/*
9464  * Based on:
9465  * Ext JS Library 1.1.1
9466  * Copyright(c) 2006-2007, Ext JS, LLC.
9467  *
9468  * Originally Released Under LGPL - original licence link has changed is not relivant.
9469  *
9470  * Fork - LGPL
9471  * <script type="text/javascript">
9472  */
9473  
9474 // Base class for reading structured data from a data source.  This class is intended to be
9475 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9476
9477 /**
9478  * @class Roo.data.DataReader
9479  * Base class for reading structured data from a data source.  This class is intended to be
9480  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9481  */
9482
9483 Roo.data.DataReader = function(meta, recordType){
9484     
9485     this.meta = meta;
9486     
9487     this.recordType = recordType instanceof Array ? 
9488         Roo.data.Record.create(recordType) : recordType;
9489 };
9490
9491 Roo.data.DataReader.prototype = {
9492      /**
9493      * Create an empty record
9494      * @param {Object} data (optional) - overlay some values
9495      * @return {Roo.data.Record} record created.
9496      */
9497     newRow :  function(d) {
9498         var da =  {};
9499         this.recordType.prototype.fields.each(function(c) {
9500             switch( c.type) {
9501                 case 'int' : da[c.name] = 0; break;
9502                 case 'date' : da[c.name] = new Date(); break;
9503                 case 'float' : da[c.name] = 0.0; break;
9504                 case 'boolean' : da[c.name] = false; break;
9505                 default : da[c.name] = ""; break;
9506             }
9507             
9508         });
9509         return new this.recordType(Roo.apply(da, d));
9510     }
9511     
9512 };/*
9513  * Based on:
9514  * Ext JS Library 1.1.1
9515  * Copyright(c) 2006-2007, Ext JS, LLC.
9516  *
9517  * Originally Released Under LGPL - original licence link has changed is not relivant.
9518  *
9519  * Fork - LGPL
9520  * <script type="text/javascript">
9521  */
9522
9523 /**
9524  * @class Roo.data.DataProxy
9525  * @extends Roo.data.Observable
9526  * This class is an abstract base class for implementations which provide retrieval of
9527  * unformatted data objects.<br>
9528  * <p>
9529  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9530  * (of the appropriate type which knows how to parse the data object) to provide a block of
9531  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9532  * <p>
9533  * Custom implementations must implement the load method as described in
9534  * {@link Roo.data.HttpProxy#load}.
9535  */
9536 Roo.data.DataProxy = function(){
9537     this.addEvents({
9538         /**
9539          * @event beforeload
9540          * Fires before a network request is made to retrieve a data object.
9541          * @param {Object} This DataProxy object.
9542          * @param {Object} params The params parameter to the load function.
9543          */
9544         beforeload : true,
9545         /**
9546          * @event load
9547          * Fires before the load method's callback is called.
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          */
9552         load : true,
9553         /**
9554          * @event loadexception
9555          * Fires if an Exception occurs during data retrieval.
9556          * @param {Object} This DataProxy object.
9557          * @param {Object} o The data object.
9558          * @param {Object} arg The callback argument object passed to the load function.
9559          * @param {Object} e The Exception.
9560          */
9561         loadexception : true
9562     });
9563     Roo.data.DataProxy.superclass.constructor.call(this);
9564 };
9565
9566 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9567
9568     /**
9569      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9570      */
9571 /*
9572  * Based on:
9573  * Ext JS Library 1.1.1
9574  * Copyright(c) 2006-2007, Ext JS, LLC.
9575  *
9576  * Originally Released Under LGPL - original licence link has changed is not relivant.
9577  *
9578  * Fork - LGPL
9579  * <script type="text/javascript">
9580  */
9581 /**
9582  * @class Roo.data.MemoryProxy
9583  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9584  * to the Reader when its load method is called.
9585  * @constructor
9586  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9587  */
9588 Roo.data.MemoryProxy = function(data){
9589     if (data.data) {
9590         data = data.data;
9591     }
9592     Roo.data.MemoryProxy.superclass.constructor.call(this);
9593     this.data = data;
9594 };
9595
9596 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9597     /**
9598      * Load data from the requested source (in this case an in-memory
9599      * data object passed to the constructor), read the data object into
9600      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9601      * process that block using the passed callback.
9602      * @param {Object} params This parameter is not used by the MemoryProxy class.
9603      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9604      * object into a block of Roo.data.Records.
9605      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9606      * The function must be passed <ul>
9607      * <li>The Record block object</li>
9608      * <li>The "arg" argument from the load function</li>
9609      * <li>A boolean success indicator</li>
9610      * </ul>
9611      * @param {Object} scope The scope in which to call the callback
9612      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9613      */
9614     load : function(params, reader, callback, scope, arg){
9615         params = params || {};
9616         var result;
9617         try {
9618             result = reader.readRecords(this.data);
9619         }catch(e){
9620             this.fireEvent("loadexception", this, arg, null, e);
9621             callback.call(scope, null, arg, false);
9622             return;
9623         }
9624         callback.call(scope, result, arg, true);
9625     },
9626     
9627     // private
9628     update : function(params, records){
9629         
9630     }
9631 });/*
9632  * Based on:
9633  * Ext JS Library 1.1.1
9634  * Copyright(c) 2006-2007, Ext JS, LLC.
9635  *
9636  * Originally Released Under LGPL - original licence link has changed is not relivant.
9637  *
9638  * Fork - LGPL
9639  * <script type="text/javascript">
9640  */
9641 /**
9642  * @class Roo.data.HttpProxy
9643  * @extends Roo.data.DataProxy
9644  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9645  * configured to reference a certain URL.<br><br>
9646  * <p>
9647  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9648  * from which the running page was served.<br><br>
9649  * <p>
9650  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9651  * <p>
9652  * Be aware that to enable the browser to parse an XML document, the server must set
9653  * the Content-Type header in the HTTP response to "text/xml".
9654  * @constructor
9655  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9656  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9657  * will be used to make the request.
9658  */
9659 Roo.data.HttpProxy = function(conn){
9660     Roo.data.HttpProxy.superclass.constructor.call(this);
9661     // is conn a conn config or a real conn?
9662     this.conn = conn;
9663     this.useAjax = !conn || !conn.events;
9664   
9665 };
9666
9667 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9668     // thse are take from connection...
9669     
9670     /**
9671      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9672      */
9673     /**
9674      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9675      * extra parameters to each request made by this object. (defaults to undefined)
9676      */
9677     /**
9678      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9679      *  to each request made by this object. (defaults to undefined)
9680      */
9681     /**
9682      * @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)
9683      */
9684     /**
9685      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9686      */
9687      /**
9688      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9689      * @type Boolean
9690      */
9691   
9692
9693     /**
9694      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9695      * @type Boolean
9696      */
9697     /**
9698      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9699      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9700      * a finer-grained basis than the DataProxy events.
9701      */
9702     getConnection : function(){
9703         return this.useAjax ? Roo.Ajax : this.conn;
9704     },
9705
9706     /**
9707      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9708      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9709      * process that block using the passed callback.
9710      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9711      * for the request to the remote server.
9712      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9713      * object into a block of Roo.data.Records.
9714      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9715      * The function must be passed <ul>
9716      * <li>The Record block object</li>
9717      * <li>The "arg" argument from the load function</li>
9718      * <li>A boolean success indicator</li>
9719      * </ul>
9720      * @param {Object} scope The scope in which to call the callback
9721      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9722      */
9723     load : function(params, reader, callback, scope, arg){
9724         if(this.fireEvent("beforeload", this, params) !== false){
9725             var  o = {
9726                 params : params || {},
9727                 request: {
9728                     callback : callback,
9729                     scope : scope,
9730                     arg : arg
9731                 },
9732                 reader: reader,
9733                 callback : this.loadResponse,
9734                 scope: this
9735             };
9736             if(this.useAjax){
9737                 Roo.applyIf(o, this.conn);
9738                 if(this.activeRequest){
9739                     Roo.Ajax.abort(this.activeRequest);
9740                 }
9741                 this.activeRequest = Roo.Ajax.request(o);
9742             }else{
9743                 this.conn.request(o);
9744             }
9745         }else{
9746             callback.call(scope||this, null, arg, false);
9747         }
9748     },
9749
9750     // private
9751     loadResponse : function(o, success, response){
9752         delete this.activeRequest;
9753         if(!success){
9754             this.fireEvent("loadexception", this, o, response);
9755             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9756             return;
9757         }
9758         var result;
9759         try {
9760             result = o.reader.read(response);
9761         }catch(e){
9762             this.fireEvent("loadexception", this, o, response, e);
9763             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9764             return;
9765         }
9766         
9767         this.fireEvent("load", this, o, o.request.arg);
9768         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9769     },
9770
9771     // private
9772     update : function(dataSet){
9773
9774     },
9775
9776     // private
9777     updateResponse : function(dataSet){
9778
9779     }
9780 });/*
9781  * Based on:
9782  * Ext JS Library 1.1.1
9783  * Copyright(c) 2006-2007, Ext JS, LLC.
9784  *
9785  * Originally Released Under LGPL - original licence link has changed is not relivant.
9786  *
9787  * Fork - LGPL
9788  * <script type="text/javascript">
9789  */
9790
9791 /**
9792  * @class Roo.data.ScriptTagProxy
9793  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9794  * other than the originating domain of the running page.<br><br>
9795  * <p>
9796  * <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
9797  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9798  * <p>
9799  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9800  * source code that is used as the source inside a &lt;script> tag.<br><br>
9801  * <p>
9802  * In order for the browser to process the returned data, the server must wrap the data object
9803  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9804  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9805  * depending on whether the callback name was passed:
9806  * <p>
9807  * <pre><code>
9808 boolean scriptTag = false;
9809 String cb = request.getParameter("callback");
9810 if (cb != null) {
9811     scriptTag = true;
9812     response.setContentType("text/javascript");
9813 } else {
9814     response.setContentType("application/x-json");
9815 }
9816 Writer out = response.getWriter();
9817 if (scriptTag) {
9818     out.write(cb + "(");
9819 }
9820 out.print(dataBlock.toJsonString());
9821 if (scriptTag) {
9822     out.write(");");
9823 }
9824 </pre></code>
9825  *
9826  * @constructor
9827  * @param {Object} config A configuration object.
9828  */
9829 Roo.data.ScriptTagProxy = function(config){
9830     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9831     Roo.apply(this, config);
9832     this.head = document.getElementsByTagName("head")[0];
9833 };
9834
9835 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9836
9837 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9838     /**
9839      * @cfg {String} url The URL from which to request the data object.
9840      */
9841     /**
9842      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9843      */
9844     timeout : 30000,
9845     /**
9846      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9847      * the server the name of the callback function set up by the load call to process the returned data object.
9848      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9849      * javascript output which calls this named function passing the data object as its only parameter.
9850      */
9851     callbackParam : "callback",
9852     /**
9853      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9854      * name to the request.
9855      */
9856     nocache : true,
9857
9858     /**
9859      * Load data from the configured URL, read the data object into
9860      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9861      * process that block using the passed callback.
9862      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9863      * for the request to the remote server.
9864      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9865      * object into a block of Roo.data.Records.
9866      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9867      * The function must be passed <ul>
9868      * <li>The Record block object</li>
9869      * <li>The "arg" argument from the load function</li>
9870      * <li>A boolean success indicator</li>
9871      * </ul>
9872      * @param {Object} scope The scope in which to call the callback
9873      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9874      */
9875     load : function(params, reader, callback, scope, arg){
9876         if(this.fireEvent("beforeload", this, params) !== false){
9877
9878             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9879
9880             var url = this.url;
9881             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9882             if(this.nocache){
9883                 url += "&_dc=" + (new Date().getTime());
9884             }
9885             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9886             var trans = {
9887                 id : transId,
9888                 cb : "stcCallback"+transId,
9889                 scriptId : "stcScript"+transId,
9890                 params : params,
9891                 arg : arg,
9892                 url : url,
9893                 callback : callback,
9894                 scope : scope,
9895                 reader : reader
9896             };
9897             var conn = this;
9898
9899             window[trans.cb] = function(o){
9900                 conn.handleResponse(o, trans);
9901             };
9902
9903             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9904
9905             if(this.autoAbort !== false){
9906                 this.abort();
9907             }
9908
9909             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9910
9911             var script = document.createElement("script");
9912             script.setAttribute("src", url);
9913             script.setAttribute("type", "text/javascript");
9914             script.setAttribute("id", trans.scriptId);
9915             this.head.appendChild(script);
9916
9917             this.trans = trans;
9918         }else{
9919             callback.call(scope||this, null, arg, false);
9920         }
9921     },
9922
9923     // private
9924     isLoading : function(){
9925         return this.trans ? true : false;
9926     },
9927
9928     /**
9929      * Abort the current server request.
9930      */
9931     abort : function(){
9932         if(this.isLoading()){
9933             this.destroyTrans(this.trans);
9934         }
9935     },
9936
9937     // private
9938     destroyTrans : function(trans, isLoaded){
9939         this.head.removeChild(document.getElementById(trans.scriptId));
9940         clearTimeout(trans.timeoutId);
9941         if(isLoaded){
9942             window[trans.cb] = undefined;
9943             try{
9944                 delete window[trans.cb];
9945             }catch(e){}
9946         }else{
9947             // if hasn't been loaded, wait for load to remove it to prevent script error
9948             window[trans.cb] = function(){
9949                 window[trans.cb] = undefined;
9950                 try{
9951                     delete window[trans.cb];
9952                 }catch(e){}
9953             };
9954         }
9955     },
9956
9957     // private
9958     handleResponse : function(o, trans){
9959         this.trans = false;
9960         this.destroyTrans(trans, true);
9961         var result;
9962         try {
9963             result = trans.reader.readRecords(o);
9964         }catch(e){
9965             this.fireEvent("loadexception", this, o, trans.arg, e);
9966             trans.callback.call(trans.scope||window, null, trans.arg, false);
9967             return;
9968         }
9969         this.fireEvent("load", this, o, trans.arg);
9970         trans.callback.call(trans.scope||window, result, trans.arg, true);
9971     },
9972
9973     // private
9974     handleFailure : function(trans){
9975         this.trans = false;
9976         this.destroyTrans(trans, false);
9977         this.fireEvent("loadexception", this, null, trans.arg);
9978         trans.callback.call(trans.scope||window, null, trans.arg, false);
9979     }
9980 });/*
9981  * Based on:
9982  * Ext JS Library 1.1.1
9983  * Copyright(c) 2006-2007, Ext JS, LLC.
9984  *
9985  * Originally Released Under LGPL - original licence link has changed is not relivant.
9986  *
9987  * Fork - LGPL
9988  * <script type="text/javascript">
9989  */
9990
9991 /**
9992  * @class Roo.data.JsonReader
9993  * @extends Roo.data.DataReader
9994  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9995  * based on mappings in a provided Roo.data.Record constructor.
9996  * 
9997  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9998  * in the reply previously. 
9999  * 
10000  * <p>
10001  * Example code:
10002  * <pre><code>
10003 var RecordDef = Roo.data.Record.create([
10004     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10005     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10006 ]);
10007 var myReader = new Roo.data.JsonReader({
10008     totalProperty: "results",    // The property which contains the total dataset size (optional)
10009     root: "rows",                // The property which contains an Array of row objects
10010     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10011 }, RecordDef);
10012 </code></pre>
10013  * <p>
10014  * This would consume a JSON file like this:
10015  * <pre><code>
10016 { 'results': 2, 'rows': [
10017     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10018     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10019 }
10020 </code></pre>
10021  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10022  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10023  * paged from the remote server.
10024  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10025  * @cfg {String} root name of the property which contains the Array of row objects.
10026  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10027  * @constructor
10028  * Create a new JsonReader
10029  * @param {Object} meta Metadata configuration options
10030  * @param {Object} recordType Either an Array of field definition objects,
10031  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10032  */
10033 Roo.data.JsonReader = function(meta, recordType){
10034     
10035     meta = meta || {};
10036     // set some defaults:
10037     Roo.applyIf(meta, {
10038         totalProperty: 'total',
10039         successProperty : 'success',
10040         root : 'data',
10041         id : 'id'
10042     });
10043     
10044     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10045 };
10046 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10047     
10048     /**
10049      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10050      * Used by Store query builder to append _requestMeta to params.
10051      * 
10052      */
10053     metaFromRemote : false,
10054     /**
10055      * This method is only used by a DataProxy which has retrieved data from a remote server.
10056      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10057      * @return {Object} data A data block which is used by an Roo.data.Store object as
10058      * a cache of Roo.data.Records.
10059      */
10060     read : function(response){
10061         var json = response.responseText;
10062        
10063         var o = /* eval:var:o */ eval("("+json+")");
10064         if(!o) {
10065             throw {message: "JsonReader.read: Json object not found"};
10066         }
10067         
10068         if(o.metaData){
10069             
10070             delete this.ef;
10071             this.metaFromRemote = true;
10072             this.meta = o.metaData;
10073             this.recordType = Roo.data.Record.create(o.metaData.fields);
10074             this.onMetaChange(this.meta, this.recordType, o);
10075         }
10076         return this.readRecords(o);
10077     },
10078
10079     // private function a store will implement
10080     onMetaChange : function(meta, recordType, o){
10081
10082     },
10083
10084     /**
10085          * @ignore
10086          */
10087     simpleAccess: function(obj, subsc) {
10088         return obj[subsc];
10089     },
10090
10091         /**
10092          * @ignore
10093          */
10094     getJsonAccessor: function(){
10095         var re = /[\[\.]/;
10096         return function(expr) {
10097             try {
10098                 return(re.test(expr))
10099                     ? new Function("obj", "return obj." + expr)
10100                     : function(obj){
10101                         return obj[expr];
10102                     };
10103             } catch(e){}
10104             return Roo.emptyFn;
10105         };
10106     }(),
10107
10108     /**
10109      * Create a data block containing Roo.data.Records from an XML document.
10110      * @param {Object} o An object which contains an Array of row objects in the property specified
10111      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10112      * which contains the total size of the dataset.
10113      * @return {Object} data A data block which is used by an Roo.data.Store object as
10114      * a cache of Roo.data.Records.
10115      */
10116     readRecords : function(o){
10117         /**
10118          * After any data loads, the raw JSON data is available for further custom processing.
10119          * @type Object
10120          */
10121         this.o = o;
10122         var s = this.meta, Record = this.recordType,
10123             f = Record.prototype.fields, fi = f.items, fl = f.length;
10124
10125 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10126         if (!this.ef) {
10127             if(s.totalProperty) {
10128                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10129                 }
10130                 if(s.successProperty) {
10131                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10132                 }
10133                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10134                 if (s.id) {
10135                         var g = this.getJsonAccessor(s.id);
10136                         this.getId = function(rec) {
10137                                 var r = g(rec);  
10138                                 return (r === undefined || r === "") ? null : r;
10139                         };
10140                 } else {
10141                         this.getId = function(){return null;};
10142                 }
10143             this.ef = [];
10144             for(var jj = 0; jj < fl; jj++){
10145                 f = fi[jj];
10146                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10147                 this.ef[jj] = this.getJsonAccessor(map);
10148             }
10149         }
10150
10151         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10152         if(s.totalProperty){
10153             var vt = parseInt(this.getTotal(o), 10);
10154             if(!isNaN(vt)){
10155                 totalRecords = vt;
10156             }
10157         }
10158         if(s.successProperty){
10159             var vs = this.getSuccess(o);
10160             if(vs === false || vs === 'false'){
10161                 success = false;
10162             }
10163         }
10164         var records = [];
10165             for(var i = 0; i < c; i++){
10166                     var n = root[i];
10167                 var values = {};
10168                 var id = this.getId(n);
10169                 for(var j = 0; j < fl; j++){
10170                     f = fi[j];
10171                 var v = this.ef[j](n);
10172                 if (!f.convert) {
10173                     Roo.log('missing convert for ' + f.name);
10174                     Roo.log(f);
10175                     continue;
10176                 }
10177                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10178                 }
10179                 var record = new Record(values, id);
10180                 record.json = n;
10181                 records[i] = record;
10182             }
10183             return {
10184             raw : o,
10185                 success : success,
10186                 records : records,
10187                 totalRecords : totalRecords
10188             };
10189     }
10190 });/*
10191  * Based on:
10192  * Ext JS Library 1.1.1
10193  * Copyright(c) 2006-2007, Ext JS, LLC.
10194  *
10195  * Originally Released Under LGPL - original licence link has changed is not relivant.
10196  *
10197  * Fork - LGPL
10198  * <script type="text/javascript">
10199  */
10200
10201 /**
10202  * @class Roo.data.ArrayReader
10203  * @extends Roo.data.DataReader
10204  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10205  * Each element of that Array represents a row of data fields. The
10206  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10207  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10208  * <p>
10209  * Example code:.
10210  * <pre><code>
10211 var RecordDef = Roo.data.Record.create([
10212     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10213     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10214 ]);
10215 var myReader = new Roo.data.ArrayReader({
10216     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10217 }, RecordDef);
10218 </code></pre>
10219  * <p>
10220  * This would consume an Array like this:
10221  * <pre><code>
10222 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10223   </code></pre>
10224  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10225  * @constructor
10226  * Create a new JsonReader
10227  * @param {Object} meta Metadata configuration options.
10228  * @param {Object} recordType Either an Array of field definition objects
10229  * as specified to {@link Roo.data.Record#create},
10230  * or an {@link Roo.data.Record} object
10231  * created using {@link Roo.data.Record#create}.
10232  */
10233 Roo.data.ArrayReader = function(meta, recordType){
10234     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10235 };
10236
10237 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10238     /**
10239      * Create a data block containing Roo.data.Records from an XML document.
10240      * @param {Object} o An Array of row objects which represents the dataset.
10241      * @return {Object} data A data block which is used by an Roo.data.Store object as
10242      * a cache of Roo.data.Records.
10243      */
10244     readRecords : function(o){
10245         var sid = this.meta ? this.meta.id : null;
10246         var recordType = this.recordType, fields = recordType.prototype.fields;
10247         var records = [];
10248         var root = o;
10249             for(var i = 0; i < root.length; i++){
10250                     var n = root[i];
10251                 var values = {};
10252                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10253                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10254                 var f = fields.items[j];
10255                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10256                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10257                 v = f.convert(v);
10258                 values[f.name] = v;
10259             }
10260                 var record = new recordType(values, id);
10261                 record.json = n;
10262                 records[records.length] = record;
10263             }
10264             return {
10265                 records : records,
10266                 totalRecords : records.length
10267             };
10268     }
10269 });/*
10270  * - LGPL
10271  * * 
10272  */
10273
10274 /**
10275  * @class Roo.bootstrap.ComboBox
10276  * @extends Roo.bootstrap.TriggerField
10277  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10278  * @cfg {Boolean} append (true|false) default false
10279  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10280  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10281  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10282  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10283  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10284  * @constructor
10285  * Create a new ComboBox.
10286  * @param {Object} config Configuration options
10287  */
10288 Roo.bootstrap.ComboBox = function(config){
10289     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10290     this.addEvents({
10291         /**
10292          * @event expand
10293          * Fires when the dropdown list is expanded
10294              * @param {Roo.bootstrap.ComboBox} combo This combo box
10295              */
10296         'expand' : true,
10297         /**
10298          * @event collapse
10299          * Fires when the dropdown list is collapsed
10300              * @param {Roo.bootstrap.ComboBox} combo This combo box
10301              */
10302         'collapse' : true,
10303         /**
10304          * @event beforeselect
10305          * Fires before a list item is selected. Return false to cancel the selection.
10306              * @param {Roo.bootstrap.ComboBox} combo This combo box
10307              * @param {Roo.data.Record} record The data record returned from the underlying store
10308              * @param {Number} index The index of the selected item in the dropdown list
10309              */
10310         'beforeselect' : true,
10311         /**
10312          * @event select
10313          * Fires when a list item is selected
10314              * @param {Roo.bootstrap.ComboBox} combo This combo box
10315              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10316              * @param {Number} index The index of the selected item in the dropdown list
10317              */
10318         'select' : true,
10319         /**
10320          * @event beforequery
10321          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10322          * The event object passed has these properties:
10323              * @param {Roo.bootstrap.ComboBox} combo This combo box
10324              * @param {String} query The query
10325              * @param {Boolean} forceAll true to force "all" query
10326              * @param {Boolean} cancel true to cancel the query
10327              * @param {Object} e The query event object
10328              */
10329         'beforequery': true,
10330          /**
10331          * @event add
10332          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10333              * @param {Roo.bootstrap.ComboBox} combo This combo box
10334              */
10335         'add' : true,
10336         /**
10337          * @event edit
10338          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10339              * @param {Roo.bootstrap.ComboBox} combo This combo box
10340              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10341              */
10342         'edit' : true,
10343         /**
10344          * @event remove
10345          * Fires when the remove value from the combobox array
10346              * @param {Roo.bootstrap.ComboBox} combo This combo box
10347              */
10348         'remove' : true
10349         
10350     });
10351     
10352     this.item = [];
10353     this.tickItems = [];
10354     
10355     this.selectedIndex = -1;
10356     if(this.mode == 'local'){
10357         if(config.queryDelay === undefined){
10358             this.queryDelay = 10;
10359         }
10360         if(config.minChars === undefined){
10361             this.minChars = 0;
10362         }
10363     }
10364 };
10365
10366 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10367      
10368     /**
10369      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10370      * rendering into an Roo.Editor, defaults to false)
10371      */
10372     /**
10373      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10374      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10375      */
10376     /**
10377      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10378      */
10379     /**
10380      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10381      * the dropdown list (defaults to undefined, with no header element)
10382      */
10383
10384      /**
10385      * @cfg {String/Roo.Template} tpl The template to use to render the output
10386      */
10387      
10388      /**
10389      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10390      */
10391     listWidth: undefined,
10392     /**
10393      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10394      * mode = 'remote' or 'text' if mode = 'local')
10395      */
10396     displayField: undefined,
10397     /**
10398      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10399      * mode = 'remote' or 'value' if mode = 'local'). 
10400      * Note: use of a valueField requires the user make a selection
10401      * in order for a value to be mapped.
10402      */
10403     valueField: undefined,
10404     
10405     
10406     /**
10407      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10408      * field's data value (defaults to the underlying DOM element's name)
10409      */
10410     hiddenName: undefined,
10411     /**
10412      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10413      */
10414     listClass: '',
10415     /**
10416      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10417      */
10418     selectedClass: 'active',
10419     
10420     /**
10421      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10422      */
10423     shadow:'sides',
10424     /**
10425      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10426      * anchor positions (defaults to 'tl-bl')
10427      */
10428     listAlign: 'tl-bl?',
10429     /**
10430      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10431      */
10432     maxHeight: 300,
10433     /**
10434      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10435      * query specified by the allQuery config option (defaults to 'query')
10436      */
10437     triggerAction: 'query',
10438     /**
10439      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10440      * (defaults to 4, does not apply if editable = false)
10441      */
10442     minChars : 4,
10443     /**
10444      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10445      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10446      */
10447     typeAhead: false,
10448     /**
10449      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10450      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10451      */
10452     queryDelay: 500,
10453     /**
10454      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10455      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10456      */
10457     pageSize: 0,
10458     /**
10459      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10460      * when editable = true (defaults to false)
10461      */
10462     selectOnFocus:false,
10463     /**
10464      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10465      */
10466     queryParam: 'query',
10467     /**
10468      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10469      * when mode = 'remote' (defaults to 'Loading...')
10470      */
10471     loadingText: 'Loading...',
10472     /**
10473      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10474      */
10475     resizable: false,
10476     /**
10477      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10478      */
10479     handleHeight : 8,
10480     /**
10481      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10482      * traditional select (defaults to true)
10483      */
10484     editable: true,
10485     /**
10486      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10487      */
10488     allQuery: '',
10489     /**
10490      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10491      */
10492     mode: 'remote',
10493     /**
10494      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10495      * listWidth has a higher value)
10496      */
10497     minListWidth : 70,
10498     /**
10499      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10500      * allow the user to set arbitrary text into the field (defaults to false)
10501      */
10502     forceSelection:false,
10503     /**
10504      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10505      * if typeAhead = true (defaults to 250)
10506      */
10507     typeAheadDelay : 250,
10508     /**
10509      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10510      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10511      */
10512     valueNotFoundText : undefined,
10513     /**
10514      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10515      */
10516     blockFocus : false,
10517     
10518     /**
10519      * @cfg {Boolean} disableClear Disable showing of clear button.
10520      */
10521     disableClear : false,
10522     /**
10523      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10524      */
10525     alwaysQuery : false,
10526     
10527     /**
10528      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10529      */
10530     multiple : false,
10531     
10532     //private
10533     addicon : false,
10534     editicon: false,
10535     
10536     page: 0,
10537     hasQuery: false,
10538     append: false,
10539     loadNext: false,
10540     autoFocus : true,
10541     tickable : false,
10542     btnPosition : 'right',
10543     triggerList : true,
10544     showToggleBtn : true,
10545     // element that contains real text value.. (when hidden is used..)
10546     
10547     getAutoCreate : function()
10548     {
10549         var cfg = false;
10550         
10551         /*
10552          *  Normal ComboBox
10553          */
10554         if(!this.tickable){
10555             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10556             return cfg;
10557         }
10558         
10559         /*
10560          *  ComboBox with tickable selections
10561          */
10562              
10563         var align = this.labelAlign || this.parentLabelAlign();
10564         
10565         cfg = {
10566             cls : 'form-group roo-combobox-tickable' //input-group
10567         };
10568         
10569         
10570         var buttons = {
10571             tag : 'div',
10572             cls : 'tickable-buttons',
10573             cn : [
10574                 {
10575                     tag : 'button',
10576                     type : 'button',
10577                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10578                     html : 'Edit'
10579                 },
10580                 {
10581                     tag : 'button',
10582                     type : 'button',
10583                     name : 'ok',
10584                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10585                     html : 'Done'
10586                 },
10587                 {
10588                     tag : 'button',
10589                     type : 'button',
10590                     name : 'cancel',
10591                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10592                     html : 'Cancel'
10593                 }
10594             ]
10595         };
10596         
10597         var _this = this;
10598         Roo.each(buttons.cn, function(c){
10599             if (_this.size) {
10600                 c.cls += ' btn-' + _this.size;
10601             }
10602
10603             if (_this.disabled) {
10604                 c.disabled = true;
10605             }
10606         });
10607         
10608         var box = {
10609             tag: 'div',
10610             cn: [
10611                 {
10612                     tag: 'input',
10613                     type : 'hidden',
10614                     cls: 'form-hidden-field'
10615                 },
10616                 {
10617                     tag: 'ul',
10618                     cls: 'select2-choices',
10619                     cn:[
10620                         {
10621                             tag: 'li',
10622                             cls: 'select2-search-field',
10623                             cn: [
10624
10625                                 buttons
10626                             ]
10627                         }
10628                     ]
10629                 }
10630             ]
10631         }
10632         
10633         var combobox = {
10634             cls: 'select2-container input-group select2-container-multi',
10635             cn: [
10636                 box
10637 //                {
10638 //                    tag: 'ul',
10639 //                    cls: 'typeahead typeahead-long dropdown-menu',
10640 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10641 //                }
10642             ]
10643         };
10644         
10645         if (align ==='left' && this.fieldLabel.length) {
10646             
10647                 Roo.log("left and has label");
10648                 cfg.cn = [
10649                     
10650                     {
10651                         tag: 'label',
10652                         'for' :  id,
10653                         cls : 'control-label col-sm-' + this.labelWidth,
10654                         html : this.fieldLabel
10655                         
10656                     },
10657                     {
10658                         cls : "col-sm-" + (12 - this.labelWidth), 
10659                         cn: [
10660                             combobox
10661                         ]
10662                     }
10663                     
10664                 ];
10665         } else if ( this.fieldLabel.length) {
10666                 Roo.log(" label");
10667                  cfg.cn = [
10668                    
10669                     {
10670                         tag: 'label',
10671                         //cls : 'input-group-addon',
10672                         html : this.fieldLabel
10673                         
10674                     },
10675                     
10676                     combobox
10677                     
10678                 ];
10679
10680         } else {
10681             
10682                 Roo.log(" no label && no align");
10683                 cfg = combobox
10684                      
10685                 
10686         }
10687          
10688         var settings=this;
10689         ['xs','sm','md','lg'].map(function(size){
10690             if (settings[size]) {
10691                 cfg.cls += ' col-' + size + '-' + settings[size];
10692             }
10693         });
10694         
10695         return cfg;
10696         
10697     },
10698     
10699     // private
10700     initEvents: function()
10701     {
10702         
10703         if (!this.store) {
10704             throw "can not find store for combo";
10705         }
10706         this.store = Roo.factory(this.store, Roo.data);
10707         
10708         if(this.tickable){
10709             this.initTickableEvents();
10710             return;
10711         }
10712         
10713         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10714         
10715         if(this.hiddenName){
10716             
10717             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10718             
10719             this.hiddenField.dom.value =
10720                 this.hiddenValue !== undefined ? this.hiddenValue :
10721                 this.value !== undefined ? this.value : '';
10722
10723             // prevent input submission
10724             this.el.dom.removeAttribute('name');
10725             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10726              
10727              
10728         }
10729         //if(Roo.isGecko){
10730         //    this.el.dom.setAttribute('autocomplete', 'off');
10731         //}
10732         
10733         var cls = 'x-combo-list';
10734         
10735         //this.list = new Roo.Layer({
10736         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10737         //});
10738         
10739         var _this = this;
10740         
10741         (function(){
10742             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10743             _this.list.setWidth(lw);
10744         }).defer(100);
10745         
10746         this.list.on('mouseover', this.onViewOver, this);
10747         this.list.on('mousemove', this.onViewMove, this);
10748         
10749         this.list.on('scroll', this.onViewScroll, this);
10750         
10751         /*
10752         this.list.swallowEvent('mousewheel');
10753         this.assetHeight = 0;
10754
10755         if(this.title){
10756             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10757             this.assetHeight += this.header.getHeight();
10758         }
10759
10760         this.innerList = this.list.createChild({cls:cls+'-inner'});
10761         this.innerList.on('mouseover', this.onViewOver, this);
10762         this.innerList.on('mousemove', this.onViewMove, this);
10763         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10764         
10765         if(this.allowBlank && !this.pageSize && !this.disableClear){
10766             this.footer = this.list.createChild({cls:cls+'-ft'});
10767             this.pageTb = new Roo.Toolbar(this.footer);
10768            
10769         }
10770         if(this.pageSize){
10771             this.footer = this.list.createChild({cls:cls+'-ft'});
10772             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10773                     {pageSize: this.pageSize});
10774             
10775         }
10776         
10777         if (this.pageTb && this.allowBlank && !this.disableClear) {
10778             var _this = this;
10779             this.pageTb.add(new Roo.Toolbar.Fill(), {
10780                 cls: 'x-btn-icon x-btn-clear',
10781                 text: '&#160;',
10782                 handler: function()
10783                 {
10784                     _this.collapse();
10785                     _this.clearValue();
10786                     _this.onSelect(false, -1);
10787                 }
10788             });
10789         }
10790         if (this.footer) {
10791             this.assetHeight += this.footer.getHeight();
10792         }
10793         */
10794             
10795         if(!this.tpl){
10796             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10797         }
10798
10799         this.view = new Roo.View(this.list, this.tpl, {
10800             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10801         });
10802         //this.view.wrapEl.setDisplayed(false);
10803         this.view.on('click', this.onViewClick, this);
10804         
10805         
10806         
10807         this.store.on('beforeload', this.onBeforeLoad, this);
10808         this.store.on('load', this.onLoad, this);
10809         this.store.on('loadexception', this.onLoadException, this);
10810         /*
10811         if(this.resizable){
10812             this.resizer = new Roo.Resizable(this.list,  {
10813                pinned:true, handles:'se'
10814             });
10815             this.resizer.on('resize', function(r, w, h){
10816                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10817                 this.listWidth = w;
10818                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10819                 this.restrictHeight();
10820             }, this);
10821             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10822         }
10823         */
10824         if(!this.editable){
10825             this.editable = true;
10826             this.setEditable(false);
10827         }
10828         
10829         /*
10830         
10831         if (typeof(this.events.add.listeners) != 'undefined') {
10832             
10833             this.addicon = this.wrap.createChild(
10834                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10835        
10836             this.addicon.on('click', function(e) {
10837                 this.fireEvent('add', this);
10838             }, this);
10839         }
10840         if (typeof(this.events.edit.listeners) != 'undefined') {
10841             
10842             this.editicon = this.wrap.createChild(
10843                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10844             if (this.addicon) {
10845                 this.editicon.setStyle('margin-left', '40px');
10846             }
10847             this.editicon.on('click', function(e) {
10848                 
10849                 // we fire even  if inothing is selected..
10850                 this.fireEvent('edit', this, this.lastData );
10851                 
10852             }, this);
10853         }
10854         */
10855         
10856         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10857             "up" : function(e){
10858                 this.inKeyMode = true;
10859                 this.selectPrev();
10860             },
10861
10862             "down" : function(e){
10863                 if(!this.isExpanded()){
10864                     this.onTriggerClick();
10865                 }else{
10866                     this.inKeyMode = true;
10867                     this.selectNext();
10868                 }
10869             },
10870
10871             "enter" : function(e){
10872 //                this.onViewClick();
10873                 //return true;
10874                 this.collapse();
10875                 
10876                 if(this.fireEvent("specialkey", this, e)){
10877                     this.onViewClick(false);
10878                 }
10879                 
10880                 return true;
10881             },
10882
10883             "esc" : function(e){
10884                 this.collapse();
10885             },
10886
10887             "tab" : function(e){
10888                 this.collapse();
10889                 
10890                 if(this.fireEvent("specialkey", this, e)){
10891                     this.onViewClick(false);
10892                 }
10893                 
10894                 return true;
10895             },
10896
10897             scope : this,
10898
10899             doRelay : function(foo, bar, hname){
10900                 if(hname == 'down' || this.scope.isExpanded()){
10901                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10902                 }
10903                 return true;
10904             },
10905
10906             forceKeyDown: true
10907         });
10908         
10909         
10910         this.queryDelay = Math.max(this.queryDelay || 10,
10911                 this.mode == 'local' ? 10 : 250);
10912         
10913         
10914         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10915         
10916         if(this.typeAhead){
10917             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10918         }
10919         if(this.editable !== false){
10920             this.inputEl().on("keyup", this.onKeyUp, this);
10921         }
10922         if(this.forceSelection){
10923             this.inputEl().on('blur', this.doForce, this);
10924         }
10925         
10926         if(this.multiple){
10927             this.choices = this.el.select('ul.select2-choices', true).first();
10928             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10929         }
10930     },
10931     
10932     initTickableEvents: function()
10933     {   
10934         this.createList();
10935         
10936         if(this.hiddenName){
10937             
10938             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10939             
10940             this.hiddenField.dom.value =
10941                 this.hiddenValue !== undefined ? this.hiddenValue :
10942                 this.value !== undefined ? this.value : '';
10943
10944             // prevent input submission
10945             this.el.dom.removeAttribute('name');
10946             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10947              
10948              
10949         }
10950         
10951 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10952         
10953         this.choices = this.el.select('ul.select2-choices', true).first();
10954         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10955         if(this.triggerList){
10956             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10957         }
10958          
10959         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10960         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10961         
10962         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10963         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10964         
10965         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10966         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10967         
10968         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10969         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10970         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10971         
10972         this.okBtn.hide();
10973         this.cancelBtn.hide();
10974         
10975         var _this = this;
10976         
10977         (function(){
10978             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10979             _this.list.setWidth(lw);
10980         }).defer(100);
10981         
10982         this.list.on('mouseover', this.onViewOver, this);
10983         this.list.on('mousemove', this.onViewMove, this);
10984         
10985         this.list.on('scroll', this.onViewScroll, this);
10986         
10987         if(!this.tpl){
10988             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>';
10989         }
10990
10991         this.view = new Roo.View(this.list, this.tpl, {
10992             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10993         });
10994         
10995         //this.view.wrapEl.setDisplayed(false);
10996         this.view.on('click', this.onViewClick, this);
10997         
10998         
10999         
11000         this.store.on('beforeload', this.onBeforeLoad, this);
11001         this.store.on('load', this.onLoad, this);
11002         this.store.on('loadexception', this.onLoadException, this);
11003         
11004 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11005 //            "up" : function(e){
11006 //                this.inKeyMode = true;
11007 //                this.selectPrev();
11008 //            },
11009 //
11010 //            "down" : function(e){
11011 //                if(!this.isExpanded()){
11012 //                    this.onTriggerClick();
11013 //                }else{
11014 //                    this.inKeyMode = true;
11015 //                    this.selectNext();
11016 //                }
11017 //            },
11018 //
11019 //            "enter" : function(e){
11020 ////                this.onViewClick();
11021 //                //return true;
11022 //                this.collapse();
11023 //                
11024 //                if(this.fireEvent("specialkey", this, e)){
11025 //                    this.onViewClick(false);
11026 //                }
11027 //                
11028 //                return true;
11029 //            },
11030 //
11031 //            "esc" : function(e){
11032 //                this.collapse();
11033 //            },
11034 //
11035 //            "tab" : function(e){
11036 //                this.collapse();
11037 //                
11038 //                if(this.fireEvent("specialkey", this, e)){
11039 //                    this.onViewClick(false);
11040 //                }
11041 //                
11042 //                return true;
11043 //            },
11044 //
11045 //            scope : this,
11046 //
11047 //            doRelay : function(foo, bar, hname){
11048 //                if(hname == 'down' || this.scope.isExpanded()){
11049 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11050 //                }
11051 //                return true;
11052 //            },
11053 //
11054 //            forceKeyDown: true
11055 //        });
11056         
11057         
11058         this.queryDelay = Math.max(this.queryDelay || 10,
11059                 this.mode == 'local' ? 10 : 250);
11060         
11061         
11062         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11063         
11064         if(this.typeAhead){
11065             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11066         }
11067     },
11068
11069     onDestroy : function(){
11070         if(this.view){
11071             this.view.setStore(null);
11072             this.view.el.removeAllListeners();
11073             this.view.el.remove();
11074             this.view.purgeListeners();
11075         }
11076         if(this.list){
11077             this.list.dom.innerHTML  = '';
11078         }
11079         
11080         if(this.store){
11081             this.store.un('beforeload', this.onBeforeLoad, this);
11082             this.store.un('load', this.onLoad, this);
11083             this.store.un('loadexception', this.onLoadException, this);
11084         }
11085         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11086     },
11087
11088     // private
11089     fireKey : function(e){
11090         if(e.isNavKeyPress() && !this.list.isVisible()){
11091             this.fireEvent("specialkey", this, e);
11092         }
11093     },
11094
11095     // private
11096     onResize: function(w, h){
11097 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11098 //        
11099 //        if(typeof w != 'number'){
11100 //            // we do not handle it!?!?
11101 //            return;
11102 //        }
11103 //        var tw = this.trigger.getWidth();
11104 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11105 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11106 //        var x = w - tw;
11107 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11108 //            
11109 //        //this.trigger.setStyle('left', x+'px');
11110 //        
11111 //        if(this.list && this.listWidth === undefined){
11112 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11113 //            this.list.setWidth(lw);
11114 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11115 //        }
11116         
11117     
11118         
11119     },
11120
11121     /**
11122      * Allow or prevent the user from directly editing the field text.  If false is passed,
11123      * the user will only be able to select from the items defined in the dropdown list.  This method
11124      * is the runtime equivalent of setting the 'editable' config option at config time.
11125      * @param {Boolean} value True to allow the user to directly edit the field text
11126      */
11127     setEditable : function(value){
11128         if(value == this.editable){
11129             return;
11130         }
11131         this.editable = value;
11132         if(!value){
11133             this.inputEl().dom.setAttribute('readOnly', true);
11134             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11135             this.inputEl().addClass('x-combo-noedit');
11136         }else{
11137             this.inputEl().dom.setAttribute('readOnly', false);
11138             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11139             this.inputEl().removeClass('x-combo-noedit');
11140         }
11141     },
11142
11143     // private
11144     
11145     onBeforeLoad : function(combo,opts){
11146         if(!this.hasFocus){
11147             return;
11148         }
11149          if (!opts.add) {
11150             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11151          }
11152         this.restrictHeight();
11153         this.selectedIndex = -1;
11154     },
11155
11156     // private
11157     onLoad : function(){
11158         
11159         this.hasQuery = false;
11160         
11161         if(!this.hasFocus){
11162             return;
11163         }
11164         
11165         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11166             this.loading.hide();
11167         }
11168         
11169         if(this.store.getCount() > 0){
11170             this.expand();
11171 //            this.restrictHeight();
11172             if(this.lastQuery == this.allQuery){
11173                 if(this.editable && !this.tickable){
11174                     this.inputEl().dom.select();
11175                 }
11176                 
11177                 if(
11178                     !this.selectByValue(this.value, true) &&
11179                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11180                     this.store.lastOptions.add != true)
11181                 ){
11182                     this.select(0, true);
11183                 }
11184             }else{
11185                 if(this.autoFocus){
11186                     this.selectNext();
11187                 }
11188                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11189                     this.taTask.delay(this.typeAheadDelay);
11190                 }
11191             }
11192         }else{
11193             this.onEmptyResults();
11194         }
11195         
11196         //this.el.focus();
11197     },
11198     // private
11199     onLoadException : function()
11200     {
11201         this.hasQuery = false;
11202         
11203         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11204             this.loading.hide();
11205         }
11206         
11207         this.collapse();
11208         Roo.log(this.store.reader.jsonData);
11209         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11210             // fixme
11211             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11212         }
11213         
11214         
11215     },
11216     // private
11217     onTypeAhead : function(){
11218         if(this.store.getCount() > 0){
11219             var r = this.store.getAt(0);
11220             var newValue = r.data[this.displayField];
11221             var len = newValue.length;
11222             var selStart = this.getRawValue().length;
11223             
11224             if(selStart != len){
11225                 this.setRawValue(newValue);
11226                 this.selectText(selStart, newValue.length);
11227             }
11228         }
11229     },
11230
11231     // private
11232     onSelect : function(record, index){
11233         
11234         if(this.fireEvent('beforeselect', this, record, index) !== false){
11235         
11236             this.setFromData(index > -1 ? record.data : false);
11237             
11238             this.collapse();
11239             this.fireEvent('select', this, record, index);
11240         }
11241     },
11242
11243     /**
11244      * Returns the currently selected field value or empty string if no value is set.
11245      * @return {String} value The selected value
11246      */
11247     getValue : function(){
11248         
11249         if(this.multiple){
11250             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11251         }
11252         
11253         if(this.valueField){
11254             return typeof this.value != 'undefined' ? this.value : '';
11255         }else{
11256             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11257         }
11258     },
11259
11260     /**
11261      * Clears any text/value currently set in the field
11262      */
11263     clearValue : function(){
11264         if(this.hiddenField){
11265             this.hiddenField.dom.value = '';
11266         }
11267         this.value = '';
11268         this.setRawValue('');
11269         this.lastSelectionText = '';
11270         
11271     },
11272
11273     /**
11274      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11275      * will be displayed in the field.  If the value does not match the data value of an existing item,
11276      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11277      * Otherwise the field will be blank (although the value will still be set).
11278      * @param {String} value The value to match
11279      */
11280     setValue : function(v){
11281         if(this.multiple){
11282             this.syncValue();
11283             return;
11284         }
11285         
11286         var text = v;
11287         if(this.valueField){
11288             var r = this.findRecord(this.valueField, v);
11289             if(r){
11290                 text = r.data[this.displayField];
11291             }else if(this.valueNotFoundText !== undefined){
11292                 text = this.valueNotFoundText;
11293             }
11294         }
11295         this.lastSelectionText = text;
11296         if(this.hiddenField){
11297             this.hiddenField.dom.value = v;
11298         }
11299         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11300         this.value = v;
11301     },
11302     /**
11303      * @property {Object} the last set data for the element
11304      */
11305     
11306     lastData : false,
11307     /**
11308      * Sets the value of the field based on a object which is related to the record format for the store.
11309      * @param {Object} value the value to set as. or false on reset?
11310      */
11311     setFromData : function(o){
11312         
11313         if(this.multiple){
11314             this.addItem(o);
11315             return;
11316         }
11317             
11318         var dv = ''; // display value
11319         var vv = ''; // value value..
11320         this.lastData = o;
11321         if (this.displayField) {
11322             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11323         } else {
11324             // this is an error condition!!!
11325             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11326         }
11327         
11328         if(this.valueField){
11329             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11330         }
11331         
11332         if(this.hiddenField){
11333             this.hiddenField.dom.value = vv;
11334             
11335             this.lastSelectionText = dv;
11336             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11337             this.value = vv;
11338             return;
11339         }
11340         // no hidden field.. - we store the value in 'value', but still display
11341         // display field!!!!
11342         this.lastSelectionText = dv;
11343         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11344         this.value = vv;
11345         
11346         
11347     },
11348     // private
11349     reset : function(){
11350         // overridden so that last data is reset..
11351         this.setValue(this.originalValue);
11352         this.clearInvalid();
11353         this.lastData = false;
11354         if (this.view) {
11355             this.view.clearSelections();
11356         }
11357     },
11358     // private
11359     findRecord : function(prop, value){
11360         var record;
11361         if(this.store.getCount() > 0){
11362             this.store.each(function(r){
11363                 if(r.data[prop] == value){
11364                     record = r;
11365                     return false;
11366                 }
11367                 return true;
11368             });
11369         }
11370         return record;
11371     },
11372     
11373     getName: function()
11374     {
11375         // returns hidden if it's set..
11376         if (!this.rendered) {return ''};
11377         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11378         
11379     },
11380     // private
11381     onViewMove : function(e, t){
11382         this.inKeyMode = false;
11383     },
11384
11385     // private
11386     onViewOver : function(e, t){
11387         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11388             return;
11389         }
11390         var item = this.view.findItemFromChild(t);
11391         
11392         if(item){
11393             var index = this.view.indexOf(item);
11394             this.select(index, false);
11395         }
11396     },
11397
11398     // private
11399     onViewClick : function(view, doFocus, el, e)
11400     {
11401         var index = this.view.getSelectedIndexes()[0];
11402         
11403         var r = this.store.getAt(index);
11404         
11405         if(this.tickable){
11406             
11407             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11408                 return;
11409             }
11410             
11411             var rm = false;
11412             var _this = this;
11413             
11414             Roo.each(this.tickItems, function(v,k){
11415                 
11416                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11417                     _this.tickItems.splice(k, 1);
11418                     rm = true;
11419                     return;
11420                 }
11421             })
11422             
11423             if(rm){
11424                 return;
11425             }
11426             
11427             this.tickItems.push(r.data);
11428             return;
11429         }
11430         
11431         if(r){
11432             this.onSelect(r, index);
11433         }
11434         if(doFocus !== false && !this.blockFocus){
11435             this.inputEl().focus();
11436         }
11437     },
11438
11439     // private
11440     restrictHeight : function(){
11441         //this.innerList.dom.style.height = '';
11442         //var inner = this.innerList.dom;
11443         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11444         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11445         //this.list.beginUpdate();
11446         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11447         this.list.alignTo(this.inputEl(), this.listAlign);
11448         this.list.alignTo(this.inputEl(), this.listAlign);
11449         //this.list.endUpdate();
11450     },
11451
11452     // private
11453     onEmptyResults : function(){
11454         this.collapse();
11455     },
11456
11457     /**
11458      * Returns true if the dropdown list is expanded, else false.
11459      */
11460     isExpanded : function(){
11461         return this.list.isVisible();
11462     },
11463
11464     /**
11465      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11466      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11467      * @param {String} value The data value of the item to select
11468      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11469      * selected item if it is not currently in view (defaults to true)
11470      * @return {Boolean} True if the value matched an item in the list, else false
11471      */
11472     selectByValue : function(v, scrollIntoView){
11473         if(v !== undefined && v !== null){
11474             var r = this.findRecord(this.valueField || this.displayField, v);
11475             if(r){
11476                 this.select(this.store.indexOf(r), scrollIntoView);
11477                 return true;
11478             }
11479         }
11480         return false;
11481     },
11482
11483     /**
11484      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11485      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11486      * @param {Number} index The zero-based index of the list item to select
11487      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11488      * selected item if it is not currently in view (defaults to true)
11489      */
11490     select : function(index, scrollIntoView){
11491         this.selectedIndex = index;
11492         this.view.select(index);
11493         if(scrollIntoView !== false){
11494             var el = this.view.getNode(index);
11495             if(el && !this.multiple && !this.tickable){
11496                 this.list.scrollChildIntoView(el, false);
11497             }
11498         }
11499     },
11500
11501     // private
11502     selectNext : function(){
11503         var ct = this.store.getCount();
11504         if(ct > 0){
11505             if(this.selectedIndex == -1){
11506                 this.select(0);
11507             }else if(this.selectedIndex < ct-1){
11508                 this.select(this.selectedIndex+1);
11509             }
11510         }
11511     },
11512
11513     // private
11514     selectPrev : function(){
11515         var ct = this.store.getCount();
11516         if(ct > 0){
11517             if(this.selectedIndex == -1){
11518                 this.select(0);
11519             }else if(this.selectedIndex != 0){
11520                 this.select(this.selectedIndex-1);
11521             }
11522         }
11523     },
11524
11525     // private
11526     onKeyUp : function(e){
11527         if(this.editable !== false && !e.isSpecialKey()){
11528             this.lastKey = e.getKey();
11529             this.dqTask.delay(this.queryDelay);
11530         }
11531     },
11532
11533     // private
11534     validateBlur : function(){
11535         return !this.list || !this.list.isVisible();   
11536     },
11537
11538     // private
11539     initQuery : function(){
11540         this.doQuery(this.getRawValue());
11541     },
11542
11543     // private
11544     doForce : function(){
11545         if(this.inputEl().dom.value.length > 0){
11546             this.inputEl().dom.value =
11547                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11548              
11549         }
11550     },
11551
11552     /**
11553      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11554      * query allowing the query action to be canceled if needed.
11555      * @param {String} query The SQL query to execute
11556      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11557      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11558      * saved in the current store (defaults to false)
11559      */
11560     doQuery : function(q, forceAll){
11561         
11562         if(q === undefined || q === null){
11563             q = '';
11564         }
11565         var qe = {
11566             query: q,
11567             forceAll: forceAll,
11568             combo: this,
11569             cancel:false
11570         };
11571         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11572             return false;
11573         }
11574         q = qe.query;
11575         
11576         forceAll = qe.forceAll;
11577         if(forceAll === true || (q.length >= this.minChars)){
11578             
11579             this.hasQuery = true;
11580             
11581             if(this.lastQuery != q || this.alwaysQuery){
11582                 this.lastQuery = q;
11583                 if(this.mode == 'local'){
11584                     this.selectedIndex = -1;
11585                     if(forceAll){
11586                         this.store.clearFilter();
11587                     }else{
11588                         this.store.filter(this.displayField, q);
11589                     }
11590                     this.onLoad();
11591                 }else{
11592                     this.store.baseParams[this.queryParam] = q;
11593                     
11594                     var options = {params : this.getParams(q)};
11595                     
11596                     if(this.loadNext){
11597                         options.add = true;
11598                         options.params.start = this.page * this.pageSize;
11599                     }
11600                     
11601                     this.store.load(options);
11602                     /*
11603                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11604                      *  we should expand the list on onLoad
11605                      *  so command out it
11606                      */
11607 //                    this.expand();
11608                 }
11609             }else{
11610                 this.selectedIndex = -1;
11611                 this.onLoad();   
11612             }
11613         }
11614         
11615         this.loadNext = false;
11616     },
11617
11618     // private
11619     getParams : function(q){
11620         var p = {};
11621         //p[this.queryParam] = q;
11622         
11623         if(this.pageSize){
11624             p.start = 0;
11625             p.limit = this.pageSize;
11626         }
11627         return p;
11628     },
11629
11630     /**
11631      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11632      */
11633     collapse : function(){
11634         if(!this.isExpanded()){
11635             return;
11636         }
11637         
11638         this.list.hide();
11639         
11640         if(this.tickable){
11641             this.okBtn.hide();
11642             this.cancelBtn.hide();
11643             this.trigger.show();
11644         }
11645         
11646         Roo.get(document).un('mousedown', this.collapseIf, this);
11647         Roo.get(document).un('mousewheel', this.collapseIf, this);
11648         if (!this.editable) {
11649             Roo.get(document).un('keydown', this.listKeyPress, this);
11650         }
11651         this.fireEvent('collapse', this);
11652     },
11653
11654     // private
11655     collapseIf : function(e){
11656         var in_combo  = e.within(this.el);
11657         var in_list =  e.within(this.list);
11658         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11659         
11660         if (in_combo || in_list || is_list) {
11661             //e.stopPropagation();
11662             return;
11663         }
11664         
11665         if(this.tickable){
11666             this.onTickableFooterButtonClick(e, false, false);
11667         }
11668
11669         this.collapse();
11670         
11671     },
11672
11673     /**
11674      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11675      */
11676     expand : function(){
11677        
11678         if(this.isExpanded() || !this.hasFocus){
11679             return;
11680         }
11681         
11682         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11683         this.list.setWidth(lw);
11684         
11685         
11686          Roo.log('expand');
11687         
11688         this.list.show();
11689         
11690         this.restrictHeight();
11691         
11692         if(this.tickable){
11693             
11694             this.tickItems = Roo.apply([], this.item);
11695             
11696             this.okBtn.show();
11697             this.cancelBtn.show();
11698             this.trigger.hide();
11699             
11700         }
11701         
11702         Roo.get(document).on('mousedown', this.collapseIf, this);
11703         Roo.get(document).on('mousewheel', this.collapseIf, this);
11704         if (!this.editable) {
11705             Roo.get(document).on('keydown', this.listKeyPress, this);
11706         }
11707         
11708         this.fireEvent('expand', this);
11709     },
11710
11711     // private
11712     // Implements the default empty TriggerField.onTriggerClick function
11713     onTriggerClick : function(e)
11714     {
11715         Roo.log('trigger click');
11716         
11717         if(this.disabled || !this.triggerList){
11718             return;
11719         }
11720         
11721         this.page = 0;
11722         this.loadNext = false;
11723         
11724         if(this.isExpanded()){
11725             this.collapse();
11726             if (!this.blockFocus) {
11727                 this.inputEl().focus();
11728             }
11729             
11730         }else {
11731             this.hasFocus = true;
11732             if(this.triggerAction == 'all') {
11733                 this.doQuery(this.allQuery, true);
11734             } else {
11735                 this.doQuery(this.getRawValue());
11736             }
11737             if (!this.blockFocus) {
11738                 this.inputEl().focus();
11739             }
11740         }
11741     },
11742     
11743     onTickableTriggerClick : function(e)
11744     {
11745         if(this.disabled){
11746             return;
11747         }
11748         
11749         this.page = 0;
11750         this.loadNext = false;
11751         this.hasFocus = true;
11752         
11753         if(this.triggerAction == 'all') {
11754             this.doQuery(this.allQuery, true);
11755         } else {
11756             this.doQuery(this.getRawValue());
11757         }
11758     },
11759     
11760     onSearchFieldClick : function(e)
11761     {
11762         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11763             return;
11764         }
11765         
11766         this.page = 0;
11767         this.loadNext = false;
11768         this.hasFocus = true;
11769         
11770         if(this.triggerAction == 'all') {
11771             this.doQuery(this.allQuery, true);
11772         } else {
11773             this.doQuery(this.getRawValue());
11774         }
11775     },
11776     
11777     listKeyPress : function(e)
11778     {
11779         //Roo.log('listkeypress');
11780         // scroll to first matching element based on key pres..
11781         if (e.isSpecialKey()) {
11782             return false;
11783         }
11784         var k = String.fromCharCode(e.getKey()).toUpperCase();
11785         //Roo.log(k);
11786         var match  = false;
11787         var csel = this.view.getSelectedNodes();
11788         var cselitem = false;
11789         if (csel.length) {
11790             var ix = this.view.indexOf(csel[0]);
11791             cselitem  = this.store.getAt(ix);
11792             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11793                 cselitem = false;
11794             }
11795             
11796         }
11797         
11798         this.store.each(function(v) { 
11799             if (cselitem) {
11800                 // start at existing selection.
11801                 if (cselitem.id == v.id) {
11802                     cselitem = false;
11803                 }
11804                 return true;
11805             }
11806                 
11807             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11808                 match = this.store.indexOf(v);
11809                 return false;
11810             }
11811             return true;
11812         }, this);
11813         
11814         if (match === false) {
11815             return true; // no more action?
11816         }
11817         // scroll to?
11818         this.view.select(match);
11819         var sn = Roo.get(this.view.getSelectedNodes()[0])
11820         //sn.scrollIntoView(sn.dom.parentNode, false);
11821     },
11822     
11823     onViewScroll : function(e, t){
11824         
11825         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){
11826             return;
11827         }
11828         
11829         this.hasQuery = true;
11830         
11831         this.loading = this.list.select('.loading', true).first();
11832         
11833         if(this.loading === null){
11834             this.list.createChild({
11835                 tag: 'div',
11836                 cls: 'loading select2-more-results select2-active',
11837                 html: 'Loading more results...'
11838             })
11839             
11840             this.loading = this.list.select('.loading', true).first();
11841             
11842             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11843             
11844             this.loading.hide();
11845         }
11846         
11847         this.loading.show();
11848         
11849         var _combo = this;
11850         
11851         this.page++;
11852         this.loadNext = true;
11853         
11854         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11855         
11856         return;
11857     },
11858     
11859     addItem : function(o)
11860     {   
11861         var dv = ''; // display value
11862         
11863         if (this.displayField) {
11864             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11865         } else {
11866             // this is an error condition!!!
11867             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11868         }
11869         
11870         if(!dv.length){
11871             return;
11872         }
11873         
11874         var choice = this.choices.createChild({
11875             tag: 'li',
11876             cls: 'select2-search-choice',
11877             cn: [
11878                 {
11879                     tag: 'div',
11880                     html: dv
11881                 },
11882                 {
11883                     tag: 'a',
11884                     href: '#',
11885                     cls: 'select2-search-choice-close',
11886                     tabindex: '-1'
11887                 }
11888             ]
11889             
11890         }, this.searchField);
11891         
11892         var close = choice.select('a.select2-search-choice-close', true).first()
11893         
11894         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11895         
11896         this.item.push(o);
11897         
11898         this.lastData = o;
11899         
11900         this.syncValue();
11901         
11902         this.inputEl().dom.value = '';
11903         
11904     },
11905     
11906     onRemoveItem : function(e, _self, o)
11907     {
11908         e.preventDefault();
11909         var index = this.item.indexOf(o.data) * 1;
11910         
11911         if( index < 0){
11912             Roo.log('not this item?!');
11913             return;
11914         }
11915         
11916         this.item.splice(index, 1);
11917         o.item.remove();
11918         
11919         this.syncValue();
11920         
11921         this.fireEvent('remove', this, e);
11922         
11923     },
11924     
11925     syncValue : function()
11926     {
11927         if(!this.item.length){
11928             this.clearValue();
11929             return;
11930         }
11931             
11932         var value = [];
11933         var _this = this;
11934         Roo.each(this.item, function(i){
11935             if(_this.valueField){
11936                 value.push(i[_this.valueField]);
11937                 return;
11938             }
11939
11940             value.push(i);
11941         });
11942
11943         this.value = value.join(',');
11944
11945         if(this.hiddenField){
11946             this.hiddenField.dom.value = this.value;
11947         }
11948     },
11949     
11950     clearItem : function()
11951     {
11952         if(!this.multiple){
11953             return;
11954         }
11955         
11956         this.item = [];
11957         
11958         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11959            c.remove();
11960         });
11961         
11962         this.syncValue();
11963     },
11964     
11965     inputEl: function ()
11966     {
11967         if(this.tickable){
11968             return this.searchField;
11969         }
11970         return this.el.select('input.form-control',true).first();
11971     },
11972     
11973     
11974     onTickableFooterButtonClick : function(e, btn, el)
11975     {
11976         e.preventDefault();
11977         
11978         if(btn && btn.name == 'cancel'){
11979             this.tickItems = Roo.apply([], this.item);
11980             this.collapse();
11981             return;
11982         }
11983         
11984         this.clearItem();
11985         
11986         var _this = this;
11987         
11988         Roo.each(this.tickItems, function(o){
11989             _this.addItem(o);
11990         });
11991         
11992         this.collapse();
11993         
11994     }
11995     
11996     
11997
11998     /** 
11999     * @cfg {Boolean} grow 
12000     * @hide 
12001     */
12002     /** 
12003     * @cfg {Number} growMin 
12004     * @hide 
12005     */
12006     /** 
12007     * @cfg {Number} growMax 
12008     * @hide 
12009     */
12010     /**
12011      * @hide
12012      * @method autoSize
12013      */
12014 });
12015 /*
12016  * Based on:
12017  * Ext JS Library 1.1.1
12018  * Copyright(c) 2006-2007, Ext JS, LLC.
12019  *
12020  * Originally Released Under LGPL - original licence link has changed is not relivant.
12021  *
12022  * Fork - LGPL
12023  * <script type="text/javascript">
12024  */
12025
12026 /**
12027  * @class Roo.View
12028  * @extends Roo.util.Observable
12029  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12030  * This class also supports single and multi selection modes. <br>
12031  * Create a data model bound view:
12032  <pre><code>
12033  var store = new Roo.data.Store(...);
12034
12035  var view = new Roo.View({
12036     el : "my-element",
12037     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12038  
12039     singleSelect: true,
12040     selectedClass: "ydataview-selected",
12041     store: store
12042  });
12043
12044  // listen for node click?
12045  view.on("click", function(vw, index, node, e){
12046  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12047  });
12048
12049  // load XML data
12050  dataModel.load("foobar.xml");
12051  </code></pre>
12052  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12053  * <br><br>
12054  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12055  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12056  * 
12057  * Note: old style constructor is still suported (container, template, config)
12058  * 
12059  * @constructor
12060  * Create a new View
12061  * @param {Object} config The config object
12062  * 
12063  */
12064 Roo.View = function(config, depreciated_tpl, depreciated_config){
12065     
12066     this.parent = false;
12067     
12068     if (typeof(depreciated_tpl) == 'undefined') {
12069         // new way.. - universal constructor.
12070         Roo.apply(this, config);
12071         this.el  = Roo.get(this.el);
12072     } else {
12073         // old format..
12074         this.el  = Roo.get(config);
12075         this.tpl = depreciated_tpl;
12076         Roo.apply(this, depreciated_config);
12077     }
12078     this.wrapEl  = this.el.wrap().wrap();
12079     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12080     
12081     
12082     if(typeof(this.tpl) == "string"){
12083         this.tpl = new Roo.Template(this.tpl);
12084     } else {
12085         // support xtype ctors..
12086         this.tpl = new Roo.factory(this.tpl, Roo);
12087     }
12088     
12089     
12090     this.tpl.compile();
12091     
12092     /** @private */
12093     this.addEvents({
12094         /**
12095          * @event beforeclick
12096          * Fires before a click is processed. Returns false to cancel the default action.
12097          * @param {Roo.View} this
12098          * @param {Number} index The index of the target node
12099          * @param {HTMLElement} node The target node
12100          * @param {Roo.EventObject} e The raw event object
12101          */
12102             "beforeclick" : true,
12103         /**
12104          * @event click
12105          * Fires when a template node is clicked.
12106          * @param {Roo.View} this
12107          * @param {Number} index The index of the target node
12108          * @param {HTMLElement} node The target node
12109          * @param {Roo.EventObject} e The raw event object
12110          */
12111             "click" : true,
12112         /**
12113          * @event dblclick
12114          * Fires when a template node is double clicked.
12115          * @param {Roo.View} this
12116          * @param {Number} index The index of the target node
12117          * @param {HTMLElement} node The target node
12118          * @param {Roo.EventObject} e The raw event object
12119          */
12120             "dblclick" : true,
12121         /**
12122          * @event contextmenu
12123          * Fires when a template node is right clicked.
12124          * @param {Roo.View} this
12125          * @param {Number} index The index of the target node
12126          * @param {HTMLElement} node The target node
12127          * @param {Roo.EventObject} e The raw event object
12128          */
12129             "contextmenu" : true,
12130         /**
12131          * @event selectionchange
12132          * Fires when the selected nodes change.
12133          * @param {Roo.View} this
12134          * @param {Array} selections Array of the selected nodes
12135          */
12136             "selectionchange" : true,
12137     
12138         /**
12139          * @event beforeselect
12140          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12141          * @param {Roo.View} this
12142          * @param {HTMLElement} node The node to be selected
12143          * @param {Array} selections Array of currently selected nodes
12144          */
12145             "beforeselect" : true,
12146         /**
12147          * @event preparedata
12148          * Fires on every row to render, to allow you to change the data.
12149          * @param {Roo.View} this
12150          * @param {Object} data to be rendered (change this)
12151          */
12152           "preparedata" : true
12153           
12154           
12155         });
12156
12157
12158
12159     this.el.on({
12160         "click": this.onClick,
12161         "dblclick": this.onDblClick,
12162         "contextmenu": this.onContextMenu,
12163         scope:this
12164     });
12165
12166     this.selections = [];
12167     this.nodes = [];
12168     this.cmp = new Roo.CompositeElementLite([]);
12169     if(this.store){
12170         this.store = Roo.factory(this.store, Roo.data);
12171         this.setStore(this.store, true);
12172     }
12173     
12174     if ( this.footer && this.footer.xtype) {
12175            
12176          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12177         
12178         this.footer.dataSource = this.store
12179         this.footer.container = fctr;
12180         this.footer = Roo.factory(this.footer, Roo);
12181         fctr.insertFirst(this.el);
12182         
12183         // this is a bit insane - as the paging toolbar seems to detach the el..
12184 //        dom.parentNode.parentNode.parentNode
12185          // they get detached?
12186     }
12187     
12188     
12189     Roo.View.superclass.constructor.call(this);
12190     
12191     
12192 };
12193
12194 Roo.extend(Roo.View, Roo.util.Observable, {
12195     
12196      /**
12197      * @cfg {Roo.data.Store} store Data store to load data from.
12198      */
12199     store : false,
12200     
12201     /**
12202      * @cfg {String|Roo.Element} el The container element.
12203      */
12204     el : '',
12205     
12206     /**
12207      * @cfg {String|Roo.Template} tpl The template used by this View 
12208      */
12209     tpl : false,
12210     /**
12211      * @cfg {String} dataName the named area of the template to use as the data area
12212      *                          Works with domtemplates roo-name="name"
12213      */
12214     dataName: false,
12215     /**
12216      * @cfg {String} selectedClass The css class to add to selected nodes
12217      */
12218     selectedClass : "x-view-selected",
12219      /**
12220      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12221      */
12222     emptyText : "",
12223     
12224     /**
12225      * @cfg {String} text to display on mask (default Loading)
12226      */
12227     mask : false,
12228     /**
12229      * @cfg {Boolean} multiSelect Allow multiple selection
12230      */
12231     multiSelect : false,
12232     /**
12233      * @cfg {Boolean} singleSelect Allow single selection
12234      */
12235     singleSelect:  false,
12236     
12237     /**
12238      * @cfg {Boolean} toggleSelect - selecting 
12239      */
12240     toggleSelect : false,
12241     
12242     /**
12243      * @cfg {Boolean} tickable - selecting 
12244      */
12245     tickable : false,
12246     
12247     /**
12248      * Returns the element this view is bound to.
12249      * @return {Roo.Element}
12250      */
12251     getEl : function(){
12252         return this.wrapEl;
12253     },
12254     
12255     
12256
12257     /**
12258      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12259      */
12260     refresh : function(){
12261         Roo.log('refresh');
12262         var t = this.tpl;
12263         
12264         // if we are using something like 'domtemplate', then
12265         // the what gets used is:
12266         // t.applySubtemplate(NAME, data, wrapping data..)
12267         // the outer template then get' applied with
12268         //     the store 'extra data'
12269         // and the body get's added to the
12270         //      roo-name="data" node?
12271         //      <span class='roo-tpl-{name}'></span> ?????
12272         
12273         
12274         
12275         this.clearSelections();
12276         this.el.update("");
12277         var html = [];
12278         var records = this.store.getRange();
12279         if(records.length < 1) {
12280             
12281             // is this valid??  = should it render a template??
12282             
12283             this.el.update(this.emptyText);
12284             return;
12285         }
12286         var el = this.el;
12287         if (this.dataName) {
12288             this.el.update(t.apply(this.store.meta)); //????
12289             el = this.el.child('.roo-tpl-' + this.dataName);
12290         }
12291         
12292         for(var i = 0, len = records.length; i < len; i++){
12293             var data = this.prepareData(records[i].data, i, records[i]);
12294             this.fireEvent("preparedata", this, data, i, records[i]);
12295             
12296             var d = Roo.apply({}, data);
12297             
12298             if(this.tickable){
12299                 Roo.apply(d, {'roo-id' : Roo.id()});
12300                 
12301                 var _this = this;
12302             
12303                 Roo.each(this.parent.item, function(item){
12304                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12305                         return;
12306                     }
12307                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12308                 });
12309             }
12310             
12311             html[html.length] = Roo.util.Format.trim(
12312                 this.dataName ?
12313                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12314                     t.apply(d)
12315             );
12316         }
12317         
12318         
12319         
12320         el.update(html.join(""));
12321         this.nodes = el.dom.childNodes;
12322         this.updateIndexes(0);
12323     },
12324     
12325
12326     /**
12327      * Function to override to reformat the data that is sent to
12328      * the template for each node.
12329      * DEPRICATED - use the preparedata event handler.
12330      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12331      * a JSON object for an UpdateManager bound view).
12332      */
12333     prepareData : function(data, index, record)
12334     {
12335         this.fireEvent("preparedata", this, data, index, record);
12336         return data;
12337     },
12338
12339     onUpdate : function(ds, record){
12340          Roo.log('on update');   
12341         this.clearSelections();
12342         var index = this.store.indexOf(record);
12343         var n = this.nodes[index];
12344         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12345         n.parentNode.removeChild(n);
12346         this.updateIndexes(index, index);
12347     },
12348
12349     
12350     
12351 // --------- FIXME     
12352     onAdd : function(ds, records, index)
12353     {
12354         Roo.log(['on Add', ds, records, index] );        
12355         this.clearSelections();
12356         if(this.nodes.length == 0){
12357             this.refresh();
12358             return;
12359         }
12360         var n = this.nodes[index];
12361         for(var i = 0, len = records.length; i < len; i++){
12362             var d = this.prepareData(records[i].data, i, records[i]);
12363             if(n){
12364                 this.tpl.insertBefore(n, d);
12365             }else{
12366                 
12367                 this.tpl.append(this.el, d);
12368             }
12369         }
12370         this.updateIndexes(index);
12371     },
12372
12373     onRemove : function(ds, record, index){
12374         Roo.log('onRemove');
12375         this.clearSelections();
12376         var el = this.dataName  ?
12377             this.el.child('.roo-tpl-' + this.dataName) :
12378             this.el; 
12379         
12380         el.dom.removeChild(this.nodes[index]);
12381         this.updateIndexes(index);
12382     },
12383
12384     /**
12385      * Refresh an individual node.
12386      * @param {Number} index
12387      */
12388     refreshNode : function(index){
12389         this.onUpdate(this.store, this.store.getAt(index));
12390     },
12391
12392     updateIndexes : function(startIndex, endIndex){
12393         var ns = this.nodes;
12394         startIndex = startIndex || 0;
12395         endIndex = endIndex || ns.length - 1;
12396         for(var i = startIndex; i <= endIndex; i++){
12397             ns[i].nodeIndex = i;
12398         }
12399     },
12400
12401     /**
12402      * Changes the data store this view uses and refresh the view.
12403      * @param {Store} store
12404      */
12405     setStore : function(store, initial){
12406         if(!initial && this.store){
12407             this.store.un("datachanged", this.refresh);
12408             this.store.un("add", this.onAdd);
12409             this.store.un("remove", this.onRemove);
12410             this.store.un("update", this.onUpdate);
12411             this.store.un("clear", this.refresh);
12412             this.store.un("beforeload", this.onBeforeLoad);
12413             this.store.un("load", this.onLoad);
12414             this.store.un("loadexception", this.onLoad);
12415         }
12416         if(store){
12417           
12418             store.on("datachanged", this.refresh, this);
12419             store.on("add", this.onAdd, this);
12420             store.on("remove", this.onRemove, this);
12421             store.on("update", this.onUpdate, this);
12422             store.on("clear", this.refresh, this);
12423             store.on("beforeload", this.onBeforeLoad, this);
12424             store.on("load", this.onLoad, this);
12425             store.on("loadexception", this.onLoad, this);
12426         }
12427         
12428         if(store){
12429             this.refresh();
12430         }
12431     },
12432     /**
12433      * onbeforeLoad - masks the loading area.
12434      *
12435      */
12436     onBeforeLoad : function(store,opts)
12437     {
12438          Roo.log('onBeforeLoad');   
12439         if (!opts.add) {
12440             this.el.update("");
12441         }
12442         this.el.mask(this.mask ? this.mask : "Loading" ); 
12443     },
12444     onLoad : function ()
12445     {
12446         this.el.unmask();
12447     },
12448     
12449
12450     /**
12451      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12452      * @param {HTMLElement} node
12453      * @return {HTMLElement} The template node
12454      */
12455     findItemFromChild : function(node){
12456         var el = this.dataName  ?
12457             this.el.child('.roo-tpl-' + this.dataName,true) :
12458             this.el.dom; 
12459         
12460         if(!node || node.parentNode == el){
12461                     return node;
12462             }
12463             var p = node.parentNode;
12464             while(p && p != el){
12465             if(p.parentNode == el){
12466                 return p;
12467             }
12468             p = p.parentNode;
12469         }
12470             return null;
12471     },
12472
12473     /** @ignore */
12474     onClick : function(e){
12475         var item = this.findItemFromChild(e.getTarget());
12476         if(item){
12477             var index = this.indexOf(item);
12478             if(this.onItemClick(item, index, e) !== false){
12479                 this.fireEvent("click", this, index, item, e);
12480             }
12481         }else{
12482             this.clearSelections();
12483         }
12484     },
12485
12486     /** @ignore */
12487     onContextMenu : function(e){
12488         var item = this.findItemFromChild(e.getTarget());
12489         if(item){
12490             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12491         }
12492     },
12493
12494     /** @ignore */
12495     onDblClick : function(e){
12496         var item = this.findItemFromChild(e.getTarget());
12497         if(item){
12498             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12499         }
12500     },
12501
12502     onItemClick : function(item, index, e)
12503     {
12504         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12505             return false;
12506         }
12507         if (this.toggleSelect) {
12508             var m = this.isSelected(item) ? 'unselect' : 'select';
12509             Roo.log(m);
12510             var _t = this;
12511             _t[m](item, true, false);
12512             return true;
12513         }
12514         if(this.multiSelect || this.singleSelect){
12515             if(this.multiSelect && e.shiftKey && this.lastSelection){
12516                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12517             }else{
12518                 this.select(item, this.multiSelect && e.ctrlKey);
12519                 this.lastSelection = item;
12520             }
12521             
12522             if(!this.tickable){
12523                 e.preventDefault();
12524             }
12525             
12526         }
12527         return true;
12528     },
12529
12530     /**
12531      * Get the number of selected nodes.
12532      * @return {Number}
12533      */
12534     getSelectionCount : function(){
12535         return this.selections.length;
12536     },
12537
12538     /**
12539      * Get the currently selected nodes.
12540      * @return {Array} An array of HTMLElements
12541      */
12542     getSelectedNodes : function(){
12543         return this.selections;
12544     },
12545
12546     /**
12547      * Get the indexes of the selected nodes.
12548      * @return {Array}
12549      */
12550     getSelectedIndexes : function(){
12551         var indexes = [], s = this.selections;
12552         for(var i = 0, len = s.length; i < len; i++){
12553             indexes.push(s[i].nodeIndex);
12554         }
12555         return indexes;
12556     },
12557
12558     /**
12559      * Clear all selections
12560      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12561      */
12562     clearSelections : function(suppressEvent){
12563         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12564             this.cmp.elements = this.selections;
12565             this.cmp.removeClass(this.selectedClass);
12566             this.selections = [];
12567             if(!suppressEvent){
12568                 this.fireEvent("selectionchange", this, this.selections);
12569             }
12570         }
12571     },
12572
12573     /**
12574      * Returns true if the passed node is selected
12575      * @param {HTMLElement/Number} node The node or node index
12576      * @return {Boolean}
12577      */
12578     isSelected : function(node){
12579         var s = this.selections;
12580         if(s.length < 1){
12581             return false;
12582         }
12583         node = this.getNode(node);
12584         return s.indexOf(node) !== -1;
12585     },
12586
12587     /**
12588      * Selects nodes.
12589      * @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
12590      * @param {Boolean} keepExisting (optional) true to keep existing selections
12591      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12592      */
12593     select : function(nodeInfo, keepExisting, suppressEvent){
12594         if(nodeInfo instanceof Array){
12595             if(!keepExisting){
12596                 this.clearSelections(true);
12597             }
12598             for(var i = 0, len = nodeInfo.length; i < len; i++){
12599                 this.select(nodeInfo[i], true, true);
12600             }
12601             return;
12602         } 
12603         var node = this.getNode(nodeInfo);
12604         if(!node || this.isSelected(node)){
12605             return; // already selected.
12606         }
12607         if(!keepExisting){
12608             this.clearSelections(true);
12609         }
12610         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12611             Roo.fly(node).addClass(this.selectedClass);
12612             this.selections.push(node);
12613             if(!suppressEvent){
12614                 this.fireEvent("selectionchange", this, this.selections);
12615             }
12616         }
12617         
12618         
12619     },
12620       /**
12621      * Unselects nodes.
12622      * @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
12623      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12624      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12625      */
12626     unselect : function(nodeInfo, keepExisting, suppressEvent)
12627     {
12628         if(nodeInfo instanceof Array){
12629             Roo.each(this.selections, function(s) {
12630                 this.unselect(s, nodeInfo);
12631             }, this);
12632             return;
12633         }
12634         var node = this.getNode(nodeInfo);
12635         if(!node || !this.isSelected(node)){
12636             Roo.log("not selected");
12637             return; // not selected.
12638         }
12639         // fireevent???
12640         var ns = [];
12641         Roo.each(this.selections, function(s) {
12642             if (s == node ) {
12643                 Roo.fly(node).removeClass(this.selectedClass);
12644
12645                 return;
12646             }
12647             ns.push(s);
12648         },this);
12649         
12650         this.selections= ns;
12651         this.fireEvent("selectionchange", this, this.selections);
12652     },
12653
12654     /**
12655      * Gets a template node.
12656      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12657      * @return {HTMLElement} The node or null if it wasn't found
12658      */
12659     getNode : function(nodeInfo){
12660         if(typeof nodeInfo == "string"){
12661             return document.getElementById(nodeInfo);
12662         }else if(typeof nodeInfo == "number"){
12663             return this.nodes[nodeInfo];
12664         }
12665         return nodeInfo;
12666     },
12667
12668     /**
12669      * Gets a range template nodes.
12670      * @param {Number} startIndex
12671      * @param {Number} endIndex
12672      * @return {Array} An array of nodes
12673      */
12674     getNodes : function(start, end){
12675         var ns = this.nodes;
12676         start = start || 0;
12677         end = typeof end == "undefined" ? ns.length - 1 : end;
12678         var nodes = [];
12679         if(start <= end){
12680             for(var i = start; i <= end; i++){
12681                 nodes.push(ns[i]);
12682             }
12683         } else{
12684             for(var i = start; i >= end; i--){
12685                 nodes.push(ns[i]);
12686             }
12687         }
12688         return nodes;
12689     },
12690
12691     /**
12692      * Finds the index of the passed node
12693      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12694      * @return {Number} The index of the node or -1
12695      */
12696     indexOf : function(node){
12697         node = this.getNode(node);
12698         if(typeof node.nodeIndex == "number"){
12699             return node.nodeIndex;
12700         }
12701         var ns = this.nodes;
12702         for(var i = 0, len = ns.length; i < len; i++){
12703             if(ns[i] == node){
12704                 return i;
12705             }
12706         }
12707         return -1;
12708     }
12709 });
12710 /*
12711  * - LGPL
12712  *
12713  * based on jquery fullcalendar
12714  * 
12715  */
12716
12717 Roo.bootstrap = Roo.bootstrap || {};
12718 /**
12719  * @class Roo.bootstrap.Calendar
12720  * @extends Roo.bootstrap.Component
12721  * Bootstrap Calendar class
12722  * @cfg {Boolean} loadMask (true|false) default false
12723  * @cfg {Object} header generate the user specific header of the calendar, default false
12724
12725  * @constructor
12726  * Create a new Container
12727  * @param {Object} config The config object
12728  */
12729
12730
12731
12732 Roo.bootstrap.Calendar = function(config){
12733     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12734      this.addEvents({
12735         /**
12736              * @event select
12737              * Fires when a date is selected
12738              * @param {DatePicker} this
12739              * @param {Date} date The selected date
12740              */
12741         'select': true,
12742         /**
12743              * @event monthchange
12744              * Fires when the displayed month changes 
12745              * @param {DatePicker} this
12746              * @param {Date} date The selected month
12747              */
12748         'monthchange': true,
12749         /**
12750              * @event evententer
12751              * Fires when mouse over an event
12752              * @param {Calendar} this
12753              * @param {event} Event
12754              */
12755         'evententer': true,
12756         /**
12757              * @event eventleave
12758              * Fires when the mouse leaves an
12759              * @param {Calendar} this
12760              * @param {event}
12761              */
12762         'eventleave': true,
12763         /**
12764              * @event eventclick
12765              * Fires when the mouse click an
12766              * @param {Calendar} this
12767              * @param {event}
12768              */
12769         'eventclick': true
12770         
12771     });
12772
12773 };
12774
12775 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12776     
12777      /**
12778      * @cfg {Number} startDay
12779      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12780      */
12781     startDay : 0,
12782     
12783     loadMask : false,
12784     
12785     header : false,
12786       
12787     getAutoCreate : function(){
12788         
12789         
12790         var fc_button = function(name, corner, style, content ) {
12791             return Roo.apply({},{
12792                 tag : 'span',
12793                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12794                          (corner.length ?
12795                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12796                             ''
12797                         ),
12798                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12799                 unselectable: 'on'
12800             });
12801         };
12802         
12803         var header = {};
12804         
12805         if(!this.header){
12806             header = {
12807                 tag : 'table',
12808                 cls : 'fc-header',
12809                 style : 'width:100%',
12810                 cn : [
12811                     {
12812                         tag: 'tr',
12813                         cn : [
12814                             {
12815                                 tag : 'td',
12816                                 cls : 'fc-header-left',
12817                                 cn : [
12818                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12819                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12820                                     { tag: 'span', cls: 'fc-header-space' },
12821                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12822
12823
12824                                 ]
12825                             },
12826
12827                             {
12828                                 tag : 'td',
12829                                 cls : 'fc-header-center',
12830                                 cn : [
12831                                     {
12832                                         tag: 'span',
12833                                         cls: 'fc-header-title',
12834                                         cn : {
12835                                             tag: 'H2',
12836                                             html : 'month / year'
12837                                         }
12838                                     }
12839
12840                                 ]
12841                             },
12842                             {
12843                                 tag : 'td',
12844                                 cls : 'fc-header-right',
12845                                 cn : [
12846                               /*      fc_button('month', 'left', '', 'month' ),
12847                                     fc_button('week', '', '', 'week' ),
12848                                     fc_button('day', 'right', '', 'day' )
12849                                 */    
12850
12851                                 ]
12852                             }
12853
12854                         ]
12855                     }
12856                 ]
12857             };
12858         }
12859         
12860         header = this.header;
12861         
12862        
12863         var cal_heads = function() {
12864             var ret = [];
12865             // fixme - handle this.
12866             
12867             for (var i =0; i < Date.dayNames.length; i++) {
12868                 var d = Date.dayNames[i];
12869                 ret.push({
12870                     tag: 'th',
12871                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12872                     html : d.substring(0,3)
12873                 });
12874                 
12875             }
12876             ret[0].cls += ' fc-first';
12877             ret[6].cls += ' fc-last';
12878             return ret;
12879         };
12880         var cal_cell = function(n) {
12881             return  {
12882                 tag: 'td',
12883                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12884                 cn : [
12885                     {
12886                         cn : [
12887                             {
12888                                 cls: 'fc-day-number',
12889                                 html: 'D'
12890                             },
12891                             {
12892                                 cls: 'fc-day-content',
12893                              
12894                                 cn : [
12895                                      {
12896                                         style: 'position: relative;' // height: 17px;
12897                                     }
12898                                 ]
12899                             }
12900                             
12901                             
12902                         ]
12903                     }
12904                 ]
12905                 
12906             }
12907         };
12908         var cal_rows = function() {
12909             
12910             var ret = []
12911             for (var r = 0; r < 6; r++) {
12912                 var row= {
12913                     tag : 'tr',
12914                     cls : 'fc-week',
12915                     cn : []
12916                 };
12917                 
12918                 for (var i =0; i < Date.dayNames.length; i++) {
12919                     var d = Date.dayNames[i];
12920                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12921
12922                 }
12923                 row.cn[0].cls+=' fc-first';
12924                 row.cn[0].cn[0].style = 'min-height:90px';
12925                 row.cn[6].cls+=' fc-last';
12926                 ret.push(row);
12927                 
12928             }
12929             ret[0].cls += ' fc-first';
12930             ret[4].cls += ' fc-prev-last';
12931             ret[5].cls += ' fc-last';
12932             return ret;
12933             
12934         };
12935         
12936         var cal_table = {
12937             tag: 'table',
12938             cls: 'fc-border-separate',
12939             style : 'width:100%',
12940             cellspacing  : 0,
12941             cn : [
12942                 { 
12943                     tag: 'thead',
12944                     cn : [
12945                         { 
12946                             tag: 'tr',
12947                             cls : 'fc-first fc-last',
12948                             cn : cal_heads()
12949                         }
12950                     ]
12951                 },
12952                 { 
12953                     tag: 'tbody',
12954                     cn : cal_rows()
12955                 }
12956                   
12957             ]
12958         };
12959          
12960          var cfg = {
12961             cls : 'fc fc-ltr',
12962             cn : [
12963                 header,
12964                 {
12965                     cls : 'fc-content',
12966                     style : "position: relative;",
12967                     cn : [
12968                         {
12969                             cls : 'fc-view fc-view-month fc-grid',
12970                             style : 'position: relative',
12971                             unselectable : 'on',
12972                             cn : [
12973                                 {
12974                                     cls : 'fc-event-container',
12975                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12976                                 },
12977                                 cal_table
12978                             ]
12979                         }
12980                     ]
12981     
12982                 }
12983            ] 
12984             
12985         };
12986         
12987          
12988         
12989         return cfg;
12990     },
12991     
12992     
12993     initEvents : function()
12994     {
12995         if(!this.store){
12996             throw "can not find store for calendar";
12997         }
12998         
12999         var mark = {
13000             tag: "div",
13001             cls:"x-dlg-mask",
13002             style: "text-align:center",
13003             cn: [
13004                 {
13005                     tag: "div",
13006                     style: "background-color:white;width:50%;margin:250 auto",
13007                     cn: [
13008                         {
13009                             tag: "img",
13010                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13011                         },
13012                         {
13013                             tag: "span",
13014                             html: "Loading"
13015                         }
13016                         
13017                     ]
13018                 }
13019             ]
13020         }
13021         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13022         
13023         var size = this.el.select('.fc-content', true).first().getSize();
13024         this.maskEl.setSize(size.width, size.height);
13025         this.maskEl.enableDisplayMode("block");
13026         if(!this.loadMask){
13027             this.maskEl.hide();
13028         }
13029         
13030         this.store = Roo.factory(this.store, Roo.data);
13031         this.store.on('load', this.onLoad, this);
13032         this.store.on('beforeload', this.onBeforeLoad, this);
13033         
13034         this.resize();
13035         
13036         this.cells = this.el.select('.fc-day',true);
13037         //Roo.log(this.cells);
13038         this.textNodes = this.el.query('.fc-day-number');
13039         this.cells.addClassOnOver('fc-state-hover');
13040         
13041         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13042         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13043         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13044         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13045         
13046         this.on('monthchange', this.onMonthChange, this);
13047         
13048         this.update(new Date().clearTime());
13049     },
13050     
13051     resize : function() {
13052         var sz  = this.el.getSize();
13053         
13054         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13055         this.el.select('.fc-day-content div',true).setHeight(34);
13056     },
13057     
13058     
13059     // private
13060     showPrevMonth : function(e){
13061         this.update(this.activeDate.add("mo", -1));
13062     },
13063     showToday : function(e){
13064         this.update(new Date().clearTime());
13065     },
13066     // private
13067     showNextMonth : function(e){
13068         this.update(this.activeDate.add("mo", 1));
13069     },
13070
13071     // private
13072     showPrevYear : function(){
13073         this.update(this.activeDate.add("y", -1));
13074     },
13075
13076     // private
13077     showNextYear : function(){
13078         this.update(this.activeDate.add("y", 1));
13079     },
13080
13081     
13082    // private
13083     update : function(date)
13084     {
13085         var vd = this.activeDate;
13086         this.activeDate = date;
13087 //        if(vd && this.el){
13088 //            var t = date.getTime();
13089 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13090 //                Roo.log('using add remove');
13091 //                
13092 //                this.fireEvent('monthchange', this, date);
13093 //                
13094 //                this.cells.removeClass("fc-state-highlight");
13095 //                this.cells.each(function(c){
13096 //                   if(c.dateValue == t){
13097 //                       c.addClass("fc-state-highlight");
13098 //                       setTimeout(function(){
13099 //                            try{c.dom.firstChild.focus();}catch(e){}
13100 //                       }, 50);
13101 //                       return false;
13102 //                   }
13103 //                   return true;
13104 //                });
13105 //                return;
13106 //            }
13107 //        }
13108         
13109         var days = date.getDaysInMonth();
13110         
13111         var firstOfMonth = date.getFirstDateOfMonth();
13112         var startingPos = firstOfMonth.getDay()-this.startDay;
13113         
13114         if(startingPos < this.startDay){
13115             startingPos += 7;
13116         }
13117         
13118         var pm = date.add(Date.MONTH, -1);
13119         var prevStart = pm.getDaysInMonth()-startingPos;
13120 //        
13121         this.cells = this.el.select('.fc-day',true);
13122         this.textNodes = this.el.query('.fc-day-number');
13123         this.cells.addClassOnOver('fc-state-hover');
13124         
13125         var cells = this.cells.elements;
13126         var textEls = this.textNodes;
13127         
13128         Roo.each(cells, function(cell){
13129             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13130         });
13131         
13132         days += startingPos;
13133
13134         // convert everything to numbers so it's fast
13135         var day = 86400000;
13136         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13137         //Roo.log(d);
13138         //Roo.log(pm);
13139         //Roo.log(prevStart);
13140         
13141         var today = new Date().clearTime().getTime();
13142         var sel = date.clearTime().getTime();
13143         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13144         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13145         var ddMatch = this.disabledDatesRE;
13146         var ddText = this.disabledDatesText;
13147         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13148         var ddaysText = this.disabledDaysText;
13149         var format = this.format;
13150         
13151         var setCellClass = function(cal, cell){
13152             cell.row = 0;
13153             cell.events = [];
13154             cell.more = [];
13155             //Roo.log('set Cell Class');
13156             cell.title = "";
13157             var t = d.getTime();
13158             
13159             //Roo.log(d);
13160             
13161             cell.dateValue = t;
13162             if(t == today){
13163                 cell.className += " fc-today";
13164                 cell.className += " fc-state-highlight";
13165                 cell.title = cal.todayText;
13166             }
13167             if(t == sel){
13168                 // disable highlight in other month..
13169                 //cell.className += " fc-state-highlight";
13170                 
13171             }
13172             // disabling
13173             if(t < min) {
13174                 cell.className = " fc-state-disabled";
13175                 cell.title = cal.minText;
13176                 return;
13177             }
13178             if(t > max) {
13179                 cell.className = " fc-state-disabled";
13180                 cell.title = cal.maxText;
13181                 return;
13182             }
13183             if(ddays){
13184                 if(ddays.indexOf(d.getDay()) != -1){
13185                     cell.title = ddaysText;
13186                     cell.className = " fc-state-disabled";
13187                 }
13188             }
13189             if(ddMatch && format){
13190                 var fvalue = d.dateFormat(format);
13191                 if(ddMatch.test(fvalue)){
13192                     cell.title = ddText.replace("%0", fvalue);
13193                     cell.className = " fc-state-disabled";
13194                 }
13195             }
13196             
13197             if (!cell.initialClassName) {
13198                 cell.initialClassName = cell.dom.className;
13199             }
13200             
13201             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13202         };
13203
13204         var i = 0;
13205         
13206         for(; i < startingPos; i++) {
13207             textEls[i].innerHTML = (++prevStart);
13208             d.setDate(d.getDate()+1);
13209             
13210             cells[i].className = "fc-past fc-other-month";
13211             setCellClass(this, cells[i]);
13212         }
13213         
13214         var intDay = 0;
13215         
13216         for(; i < days; i++){
13217             intDay = i - startingPos + 1;
13218             textEls[i].innerHTML = (intDay);
13219             d.setDate(d.getDate()+1);
13220             
13221             cells[i].className = ''; // "x-date-active";
13222             setCellClass(this, cells[i]);
13223         }
13224         var extraDays = 0;
13225         
13226         for(; i < 42; i++) {
13227             textEls[i].innerHTML = (++extraDays);
13228             d.setDate(d.getDate()+1);
13229             
13230             cells[i].className = "fc-future fc-other-month";
13231             setCellClass(this, cells[i]);
13232         }
13233         
13234         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13235         
13236         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13237         
13238         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13239         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13240         
13241         if(totalRows != 6){
13242             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13243             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13244         }
13245         
13246         this.fireEvent('monthchange', this, date);
13247         
13248         
13249         /*
13250         if(!this.internalRender){
13251             var main = this.el.dom.firstChild;
13252             var w = main.offsetWidth;
13253             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13254             Roo.fly(main).setWidth(w);
13255             this.internalRender = true;
13256             // opera does not respect the auto grow header center column
13257             // then, after it gets a width opera refuses to recalculate
13258             // without a second pass
13259             if(Roo.isOpera && !this.secondPass){
13260                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13261                 this.secondPass = true;
13262                 this.update.defer(10, this, [date]);
13263             }
13264         }
13265         */
13266         
13267     },
13268     
13269     findCell : function(dt) {
13270         dt = dt.clearTime().getTime();
13271         var ret = false;
13272         this.cells.each(function(c){
13273             //Roo.log("check " +c.dateValue + '?=' + dt);
13274             if(c.dateValue == dt){
13275                 ret = c;
13276                 return false;
13277             }
13278             return true;
13279         });
13280         
13281         return ret;
13282     },
13283     
13284     findCells : function(ev) {
13285         var s = ev.start.clone().clearTime().getTime();
13286        // Roo.log(s);
13287         var e= ev.end.clone().clearTime().getTime();
13288        // Roo.log(e);
13289         var ret = [];
13290         this.cells.each(function(c){
13291              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13292             
13293             if(c.dateValue > e){
13294                 return ;
13295             }
13296             if(c.dateValue < s){
13297                 return ;
13298             }
13299             ret.push(c);
13300         });
13301         
13302         return ret;    
13303     },
13304     
13305 //    findBestRow: function(cells)
13306 //    {
13307 //        var ret = 0;
13308 //        
13309 //        for (var i =0 ; i < cells.length;i++) {
13310 //            ret  = Math.max(cells[i].rows || 0,ret);
13311 //        }
13312 //        return ret;
13313 //        
13314 //    },
13315     
13316     
13317     addItem : function(ev)
13318     {
13319         // look for vertical location slot in
13320         var cells = this.findCells(ev);
13321         
13322 //        ev.row = this.findBestRow(cells);
13323         
13324         // work out the location.
13325         
13326         var crow = false;
13327         var rows = [];
13328         for(var i =0; i < cells.length; i++) {
13329             
13330             cells[i].row = cells[0].row;
13331             
13332             if(i == 0){
13333                 cells[i].row = cells[i].row + 1;
13334             }
13335             
13336             if (!crow) {
13337                 crow = {
13338                     start : cells[i],
13339                     end :  cells[i]
13340                 };
13341                 continue;
13342             }
13343             if (crow.start.getY() == cells[i].getY()) {
13344                 // on same row.
13345                 crow.end = cells[i];
13346                 continue;
13347             }
13348             // different row.
13349             rows.push(crow);
13350             crow = {
13351                 start: cells[i],
13352                 end : cells[i]
13353             };
13354             
13355         }
13356         
13357         rows.push(crow);
13358         ev.els = [];
13359         ev.rows = rows;
13360         ev.cells = cells;
13361         
13362         cells[0].events.push(ev);
13363         
13364         this.calevents.push(ev);
13365     },
13366     
13367     clearEvents: function() {
13368         
13369         if(!this.calevents){
13370             return;
13371         }
13372         
13373         Roo.each(this.cells.elements, function(c){
13374             c.row = 0;
13375             c.events = [];
13376             c.more = [];
13377         });
13378         
13379         Roo.each(this.calevents, function(e) {
13380             Roo.each(e.els, function(el) {
13381                 el.un('mouseenter' ,this.onEventEnter, this);
13382                 el.un('mouseleave' ,this.onEventLeave, this);
13383                 el.remove();
13384             },this);
13385         },this);
13386         
13387         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13388             e.remove();
13389         });
13390         
13391     },
13392     
13393     renderEvents: function()
13394     {   
13395         var _this = this;
13396         
13397         this.cells.each(function(c) {
13398             
13399             if(c.row < 5){
13400                 return;
13401             }
13402             
13403             var ev = c.events;
13404             
13405             var r = 4;
13406             if(c.row != c.events.length){
13407                 r = 4 - (4 - (c.row - c.events.length));
13408             }
13409             
13410             c.events = ev.slice(0, r);
13411             c.more = ev.slice(r);
13412             
13413             if(c.more.length && c.more.length == 1){
13414                 c.events.push(c.more.pop());
13415             }
13416             
13417             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13418             
13419         });
13420             
13421         this.cells.each(function(c) {
13422             
13423             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13424             
13425             
13426             for (var e = 0; e < c.events.length; e++){
13427                 var ev = c.events[e];
13428                 var rows = ev.rows;
13429                 
13430                 for(var i = 0; i < rows.length; i++) {
13431                 
13432                     // how many rows should it span..
13433
13434                     var  cfg = {
13435                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13436                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13437
13438                         unselectable : "on",
13439                         cn : [
13440                             {
13441                                 cls: 'fc-event-inner',
13442                                 cn : [
13443     //                                {
13444     //                                  tag:'span',
13445     //                                  cls: 'fc-event-time',
13446     //                                  html : cells.length > 1 ? '' : ev.time
13447     //                                },
13448                                     {
13449                                       tag:'span',
13450                                       cls: 'fc-event-title',
13451                                       html : String.format('{0}', ev.title)
13452                                     }
13453
13454
13455                                 ]
13456                             },
13457                             {
13458                                 cls: 'ui-resizable-handle ui-resizable-e',
13459                                 html : '&nbsp;&nbsp;&nbsp'
13460                             }
13461
13462                         ]
13463                     };
13464
13465                     if (i == 0) {
13466                         cfg.cls += ' fc-event-start';
13467                     }
13468                     if ((i+1) == rows.length) {
13469                         cfg.cls += ' fc-event-end';
13470                     }
13471
13472                     var ctr = _this.el.select('.fc-event-container',true).first();
13473                     var cg = ctr.createChild(cfg);
13474
13475                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13476                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13477
13478                     var r = (c.more.length) ? 1 : 0;
13479                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13480                     cg.setWidth(ebox.right - sbox.x -2);
13481
13482                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13483                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13484                     cg.on('click', _this.onEventClick, _this, ev);
13485
13486                     ev.els.push(cg);
13487                     
13488                 }
13489                 
13490             }
13491             
13492             
13493             if(c.more.length){
13494                 var  cfg = {
13495                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13496                     style : 'position: absolute',
13497                     unselectable : "on",
13498                     cn : [
13499                         {
13500                             cls: 'fc-event-inner',
13501                             cn : [
13502                                 {
13503                                   tag:'span',
13504                                   cls: 'fc-event-title',
13505                                   html : 'More'
13506                                 }
13507
13508
13509                             ]
13510                         },
13511                         {
13512                             cls: 'ui-resizable-handle ui-resizable-e',
13513                             html : '&nbsp;&nbsp;&nbsp'
13514                         }
13515
13516                     ]
13517                 };
13518
13519                 var ctr = _this.el.select('.fc-event-container',true).first();
13520                 var cg = ctr.createChild(cfg);
13521
13522                 var sbox = c.select('.fc-day-content',true).first().getBox();
13523                 var ebox = c.select('.fc-day-content',true).first().getBox();
13524                 //Roo.log(cg);
13525                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13526                 cg.setWidth(ebox.right - sbox.x -2);
13527
13528                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13529                 
13530             }
13531             
13532         });
13533         
13534         
13535         
13536     },
13537     
13538     onEventEnter: function (e, el,event,d) {
13539         this.fireEvent('evententer', this, el, event);
13540     },
13541     
13542     onEventLeave: function (e, el,event,d) {
13543         this.fireEvent('eventleave', this, el, event);
13544     },
13545     
13546     onEventClick: function (e, el,event,d) {
13547         this.fireEvent('eventclick', this, el, event);
13548     },
13549     
13550     onMonthChange: function () {
13551         this.store.load();
13552     },
13553     
13554     onMoreEventClick: function(e, el, more)
13555     {
13556         var _this = this;
13557         
13558         this.calpopover.placement = 'right';
13559         this.calpopover.setTitle('More');
13560         
13561         this.calpopover.setContent('');
13562         
13563         var ctr = this.calpopover.el.select('.popover-content', true).first();
13564         
13565         Roo.each(more, function(m){
13566             var cfg = {
13567                 cls : 'fc-event-hori fc-event-draggable',
13568                 html : m.title
13569             }
13570             var cg = ctr.createChild(cfg);
13571             
13572             cg.on('click', _this.onEventClick, _this, m);
13573         });
13574         
13575         this.calpopover.show(el);
13576         
13577         
13578     },
13579     
13580     onLoad: function () 
13581     {   
13582         this.calevents = [];
13583         var cal = this;
13584         
13585         if(this.store.getCount() > 0){
13586             this.store.data.each(function(d){
13587                cal.addItem({
13588                     id : d.data.id,
13589                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13590                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13591                     time : d.data.start_time,
13592                     title : d.data.title,
13593                     description : d.data.description,
13594                     venue : d.data.venue
13595                 });
13596             });
13597         }
13598         
13599         this.renderEvents();
13600         
13601         if(this.calevents.length && this.loadMask){
13602             this.maskEl.hide();
13603         }
13604     },
13605     
13606     onBeforeLoad: function()
13607     {
13608         this.clearEvents();
13609         if(this.loadMask){
13610             this.maskEl.show();
13611         }
13612     }
13613 });
13614
13615  
13616  /*
13617  * - LGPL
13618  *
13619  * element
13620  * 
13621  */
13622
13623 /**
13624  * @class Roo.bootstrap.Popover
13625  * @extends Roo.bootstrap.Component
13626  * Bootstrap Popover class
13627  * @cfg {String} html contents of the popover   (or false to use children..)
13628  * @cfg {String} title of popover (or false to hide)
13629  * @cfg {String} placement how it is placed
13630  * @cfg {String} trigger click || hover (or false to trigger manually)
13631  * @cfg {String} over what (parent or false to trigger manually.)
13632  * 
13633  * @constructor
13634  * Create a new Popover
13635  * @param {Object} config The config object
13636  */
13637
13638 Roo.bootstrap.Popover = function(config){
13639     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13640 };
13641
13642 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13643     
13644     title: 'Fill in a title',
13645     html: false,
13646     
13647     placement : 'right',
13648     trigger : 'hover', // hover
13649     
13650     over: 'parent',
13651     
13652     can_build_overlaid : false,
13653     
13654     getChildContainer : function()
13655     {
13656         return this.el.select('.popover-content',true).first();
13657     },
13658     
13659     getAutoCreate : function(){
13660          Roo.log('make popover?');
13661         var cfg = {
13662            cls : 'popover roo-dynamic',
13663            style: 'display:block',
13664            cn : [
13665                 {
13666                     cls : 'arrow'
13667                 },
13668                 {
13669                     cls : 'popover-inner',
13670                     cn : [
13671                         {
13672                             tag: 'h3',
13673                             cls: 'popover-title',
13674                             html : this.title
13675                         },
13676                         {
13677                             cls : 'popover-content',
13678                             html : this.html
13679                         }
13680                     ]
13681                     
13682                 }
13683            ]
13684         };
13685         
13686         return cfg;
13687     },
13688     setTitle: function(str)
13689     {
13690         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13691     },
13692     setContent: function(str)
13693     {
13694         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13695     },
13696     // as it get's added to the bottom of the page.
13697     onRender : function(ct, position)
13698     {
13699         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13700         if(!this.el){
13701             var cfg = Roo.apply({},  this.getAutoCreate());
13702             cfg.id = Roo.id();
13703             
13704             if (this.cls) {
13705                 cfg.cls += ' ' + this.cls;
13706             }
13707             if (this.style) {
13708                 cfg.style = this.style;
13709             }
13710             Roo.log("adding to ")
13711             this.el = Roo.get(document.body).createChild(cfg, position);
13712             Roo.log(this.el);
13713         }
13714         this.initEvents();
13715     },
13716     
13717     initEvents : function()
13718     {
13719         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13720         this.el.enableDisplayMode('block');
13721         this.el.hide();
13722         if (this.over === false) {
13723             return; 
13724         }
13725         if (this.triggers === false) {
13726             return;
13727         }
13728         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13729         var triggers = this.trigger ? this.trigger.split(' ') : [];
13730         Roo.each(triggers, function(trigger) {
13731         
13732             if (trigger == 'click') {
13733                 on_el.on('click', this.toggle, this);
13734             } else if (trigger != 'manual') {
13735                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13736                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13737       
13738                 on_el.on(eventIn  ,this.enter, this);
13739                 on_el.on(eventOut, this.leave, this);
13740             }
13741         }, this);
13742         
13743     },
13744     
13745     
13746     // private
13747     timeout : null,
13748     hoverState : null,
13749     
13750     toggle : function () {
13751         this.hoverState == 'in' ? this.leave() : this.enter();
13752     },
13753     
13754     enter : function () {
13755        
13756     
13757         clearTimeout(this.timeout);
13758     
13759         this.hoverState = 'in'
13760     
13761         if (!this.delay || !this.delay.show) {
13762             this.show();
13763             return 
13764         }
13765         var _t = this;
13766         this.timeout = setTimeout(function () {
13767             if (_t.hoverState == 'in') {
13768                 _t.show();
13769             }
13770         }, this.delay.show)
13771     },
13772     leave : function() {
13773         clearTimeout(this.timeout);
13774     
13775         this.hoverState = 'out'
13776     
13777         if (!this.delay || !this.delay.hide) {
13778             this.hide();
13779             return 
13780         }
13781         var _t = this;
13782         this.timeout = setTimeout(function () {
13783             if (_t.hoverState == 'out') {
13784                 _t.hide();
13785             }
13786         }, this.delay.hide)
13787     },
13788     
13789     show : function (on_el)
13790     {
13791         if (!on_el) {
13792             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13793         }
13794         // set content.
13795         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13796         if (this.html !== false) {
13797             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13798         }
13799         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13800         if (!this.title.length) {
13801             this.el.select('.popover-title',true).hide();
13802         }
13803         
13804         var placement = typeof this.placement == 'function' ?
13805             this.placement.call(this, this.el, on_el) :
13806             this.placement;
13807             
13808         var autoToken = /\s?auto?\s?/i;
13809         var autoPlace = autoToken.test(placement);
13810         if (autoPlace) {
13811             placement = placement.replace(autoToken, '') || 'top';
13812         }
13813         
13814         //this.el.detach()
13815         //this.el.setXY([0,0]);
13816         this.el.show();
13817         this.el.dom.style.display='block';
13818         this.el.addClass(placement);
13819         
13820         //this.el.appendTo(on_el);
13821         
13822         var p = this.getPosition();
13823         var box = this.el.getBox();
13824         
13825         if (autoPlace) {
13826             // fixme..
13827         }
13828         var align = Roo.bootstrap.Popover.alignment[placement]
13829         this.el.alignTo(on_el, align[0],align[1]);
13830         //var arrow = this.el.select('.arrow',true).first();
13831         //arrow.set(align[2], 
13832         
13833         this.el.addClass('in');
13834         this.hoverState = null;
13835         
13836         if (this.el.hasClass('fade')) {
13837             // fade it?
13838         }
13839         
13840     },
13841     hide : function()
13842     {
13843         this.el.setXY([0,0]);
13844         this.el.removeClass('in');
13845         this.el.hide();
13846         
13847     }
13848     
13849 });
13850
13851 Roo.bootstrap.Popover.alignment = {
13852     'left' : ['r-l', [-10,0], 'right'],
13853     'right' : ['l-r', [10,0], 'left'],
13854     'bottom' : ['t-b', [0,10], 'top'],
13855     'top' : [ 'b-t', [0,-10], 'bottom']
13856 };
13857
13858  /*
13859  * - LGPL
13860  *
13861  * Progress
13862  * 
13863  */
13864
13865 /**
13866  * @class Roo.bootstrap.Progress
13867  * @extends Roo.bootstrap.Component
13868  * Bootstrap Progress class
13869  * @cfg {Boolean} striped striped of the progress bar
13870  * @cfg {Boolean} active animated of the progress bar
13871  * 
13872  * 
13873  * @constructor
13874  * Create a new Progress
13875  * @param {Object} config The config object
13876  */
13877
13878 Roo.bootstrap.Progress = function(config){
13879     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13880 };
13881
13882 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13883     
13884     striped : false,
13885     active: false,
13886     
13887     getAutoCreate : function(){
13888         var cfg = {
13889             tag: 'div',
13890             cls: 'progress'
13891         };
13892         
13893         
13894         if(this.striped){
13895             cfg.cls += ' progress-striped';
13896         }
13897       
13898         if(this.active){
13899             cfg.cls += ' active';
13900         }
13901         
13902         
13903         return cfg;
13904     }
13905    
13906 });
13907
13908  
13909
13910  /*
13911  * - LGPL
13912  *
13913  * ProgressBar
13914  * 
13915  */
13916
13917 /**
13918  * @class Roo.bootstrap.ProgressBar
13919  * @extends Roo.bootstrap.Component
13920  * Bootstrap ProgressBar class
13921  * @cfg {Number} aria_valuenow aria-value now
13922  * @cfg {Number} aria_valuemin aria-value min
13923  * @cfg {Number} aria_valuemax aria-value max
13924  * @cfg {String} label label for the progress bar
13925  * @cfg {String} panel (success | info | warning | danger )
13926  * @cfg {String} role role of the progress bar
13927  * @cfg {String} sr_only text
13928  * 
13929  * 
13930  * @constructor
13931  * Create a new ProgressBar
13932  * @param {Object} config The config object
13933  */
13934
13935 Roo.bootstrap.ProgressBar = function(config){
13936     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13937 };
13938
13939 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13940     
13941     aria_valuenow : 0,
13942     aria_valuemin : 0,
13943     aria_valuemax : 100,
13944     label : false,
13945     panel : false,
13946     role : false,
13947     sr_only: false,
13948     
13949     getAutoCreate : function()
13950     {
13951         
13952         var cfg = {
13953             tag: 'div',
13954             cls: 'progress-bar',
13955             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13956         };
13957         
13958         if(this.sr_only){
13959             cfg.cn = {
13960                 tag: 'span',
13961                 cls: 'sr-only',
13962                 html: this.sr_only
13963             }
13964         }
13965         
13966         if(this.role){
13967             cfg.role = this.role;
13968         }
13969         
13970         if(this.aria_valuenow){
13971             cfg['aria-valuenow'] = this.aria_valuenow;
13972         }
13973         
13974         if(this.aria_valuemin){
13975             cfg['aria-valuemin'] = this.aria_valuemin;
13976         }
13977         
13978         if(this.aria_valuemax){
13979             cfg['aria-valuemax'] = this.aria_valuemax;
13980         }
13981         
13982         if(this.label && !this.sr_only){
13983             cfg.html = this.label;
13984         }
13985         
13986         if(this.panel){
13987             cfg.cls += ' progress-bar-' + this.panel;
13988         }
13989         
13990         return cfg;
13991     },
13992     
13993     update : function(aria_valuenow)
13994     {
13995         this.aria_valuenow = aria_valuenow;
13996         
13997         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13998     }
13999    
14000 });
14001
14002  
14003
14004  /*
14005  * - LGPL
14006  *
14007  * column
14008  * 
14009  */
14010
14011 /**
14012  * @class Roo.bootstrap.TabGroup
14013  * @extends Roo.bootstrap.Column
14014  * Bootstrap Column class
14015  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14016  * @cfg {Boolean} carousel true to make the group behave like a carousel
14017  * 
14018  * @constructor
14019  * Create a new TabGroup
14020  * @param {Object} config The config object
14021  */
14022
14023 Roo.bootstrap.TabGroup = function(config){
14024     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14025     if (!this.navId) {
14026         this.navId = Roo.id();
14027     }
14028     this.tabs = [];
14029     Roo.bootstrap.TabGroup.register(this);
14030     
14031 };
14032
14033 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14034     
14035     carousel : false,
14036     transition : false,
14037      
14038     getAutoCreate : function()
14039     {
14040         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14041         
14042         cfg.cls += ' tab-content';
14043         
14044         if (this.carousel) {
14045             cfg.cls += ' carousel slide';
14046             cfg.cn = [{
14047                cls : 'carousel-inner'
14048             }]
14049         }
14050         
14051         
14052         return cfg;
14053     },
14054     getChildContainer : function()
14055     {
14056         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14057     },
14058     
14059     /**
14060     * register a Navigation item
14061     * @param {Roo.bootstrap.NavItem} the navitem to add
14062     */
14063     register : function(item)
14064     {
14065         this.tabs.push( item);
14066         item.navId = this.navId; // not really needed..
14067     
14068     },
14069     
14070     getActivePanel : function()
14071     {
14072         var r = false;
14073         Roo.each(this.tabs, function(t) {
14074             if (t.active) {
14075                 r = t;
14076                 return false;
14077             }
14078             return null;
14079         });
14080         return r;
14081         
14082     },
14083     getPanelByName : function(n)
14084     {
14085         var r = false;
14086         Roo.each(this.tabs, function(t) {
14087             if (t.tabId == n) {
14088                 r = t;
14089                 return false;
14090             }
14091             return null;
14092         });
14093         return r;
14094     },
14095     indexOfPanel : function(p)
14096     {
14097         var r = false;
14098         Roo.each(this.tabs, function(t,i) {
14099             if (t.tabId == p.tabId) {
14100                 r = i;
14101                 return false;
14102             }
14103             return null;
14104         });
14105         return r;
14106     },
14107     /**
14108      * show a specific panel
14109      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14110      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14111      */
14112     showPanel : function (pan)
14113     {
14114         
14115         if (typeof(pan) == 'number') {
14116             pan = this.tabs[pan];
14117         }
14118         if (typeof(pan) == 'string') {
14119             pan = this.getPanelByName(pan);
14120         }
14121         if (pan.tabId == this.getActivePanel().tabId) {
14122             return true;
14123         }
14124         var cur = this.getActivePanel();
14125         
14126         if (false === cur.fireEvent('beforedeactivate')) {
14127             return false;
14128         }
14129         
14130         if (this.carousel) {
14131             this.transition = true;
14132             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14133             var lr = dir == 'next' ? 'left' : 'right';
14134             pan.el.addClass(dir); // or prev
14135             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14136             cur.el.addClass(lr); // or right
14137             pan.el.addClass(lr);
14138             
14139             var _this = this;
14140             cur.el.on('transitionend', function() {
14141                 Roo.log("trans end?");
14142                 
14143                 pan.el.removeClass([lr,dir]);
14144                 pan.setActive(true);
14145                 
14146                 cur.el.removeClass([lr]);
14147                 cur.setActive(false);
14148                 
14149                 _this.transition = false;
14150                 
14151             }, this, { single:  true } );
14152             return true;
14153         }
14154         
14155         cur.setActive(false);
14156         pan.setActive(true);
14157         return true;
14158         
14159     },
14160     showPanelNext : function()
14161     {
14162         var i = this.indexOfPanel(this.getActivePanel());
14163         if (i > this.tabs.length) {
14164             return;
14165         }
14166         this.showPanel(this.tabs[i+1]);
14167     },
14168     showPanelPrev : function()
14169     {
14170         var i = this.indexOfPanel(this.getActivePanel());
14171         if (i  < 1) {
14172             return;
14173         }
14174         this.showPanel(this.tabs[i-1]);
14175     }
14176     
14177     
14178   
14179 });
14180
14181  
14182
14183  
14184  
14185 Roo.apply(Roo.bootstrap.TabGroup, {
14186     
14187     groups: {},
14188      /**
14189     * register a Navigation Group
14190     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14191     */
14192     register : function(navgrp)
14193     {
14194         this.groups[navgrp.navId] = navgrp;
14195         
14196     },
14197     /**
14198     * fetch a Navigation Group based on the navigation ID
14199     * if one does not exist , it will get created.
14200     * @param {string} the navgroup to add
14201     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14202     */
14203     get: function(navId) {
14204         if (typeof(this.groups[navId]) == 'undefined') {
14205             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14206         }
14207         return this.groups[navId] ;
14208     }
14209     
14210     
14211     
14212 });
14213
14214  /*
14215  * - LGPL
14216  *
14217  * TabPanel
14218  * 
14219  */
14220
14221 /**
14222  * @class Roo.bootstrap.TabPanel
14223  * @extends Roo.bootstrap.Component
14224  * Bootstrap TabPanel class
14225  * @cfg {Boolean} active panel active
14226  * @cfg {String} html panel content
14227  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14228  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14229  * 
14230  * 
14231  * @constructor
14232  * Create a new TabPanel
14233  * @param {Object} config The config object
14234  */
14235
14236 Roo.bootstrap.TabPanel = function(config){
14237     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14238     this.addEvents({
14239         /**
14240              * @event changed
14241              * Fires when the active status changes
14242              * @param {Roo.bootstrap.TabPanel} this
14243              * @param {Boolean} state the new state
14244             
14245          */
14246         'changed': true,
14247         /**
14248              * @event beforedeactivate
14249              * Fires before a tab is de-activated - can be used to do validation on a form.
14250              * @param {Roo.bootstrap.TabPanel} this
14251              * @return {Boolean} false if there is an error
14252             
14253          */
14254         'beforedeactivate': true
14255      });
14256     
14257     this.tabId = this.tabId || Roo.id();
14258   
14259 };
14260
14261 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14262     
14263     active: false,
14264     html: false,
14265     tabId: false,
14266     navId : false,
14267     
14268     getAutoCreate : function(){
14269         var cfg = {
14270             tag: 'div',
14271             // item is needed for carousel - not sure if it has any effect otherwise
14272             cls: 'tab-pane item',
14273             html: this.html || ''
14274         };
14275         
14276         if(this.active){
14277             cfg.cls += ' active';
14278         }
14279         
14280         if(this.tabId){
14281             cfg.tabId = this.tabId;
14282         }
14283         
14284         
14285         return cfg;
14286     },
14287     
14288     initEvents:  function()
14289     {
14290         Roo.log('-------- init events on tab panel ---------');
14291         
14292         var p = this.parent();
14293         this.navId = this.navId || p.navId;
14294         
14295         if (typeof(this.navId) != 'undefined') {
14296             // not really needed.. but just in case.. parent should be a NavGroup.
14297             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14298             Roo.log(['register', tg, this]);
14299             tg.register(this);
14300         }
14301     },
14302     
14303     
14304     onRender : function(ct, position)
14305     {
14306        // Roo.log("Call onRender: " + this.xtype);
14307         
14308         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14309         
14310         
14311         
14312         
14313         
14314     },
14315     
14316     setActive: function(state)
14317     {
14318         Roo.log("panel - set active " + this.tabId + "=" + state);
14319         
14320         this.active = state;
14321         if (!state) {
14322             this.el.removeClass('active');
14323             
14324         } else  if (!this.el.hasClass('active')) {
14325             this.el.addClass('active');
14326         }
14327         this.fireEvent('changed', this, state);
14328     }
14329     
14330     
14331 });
14332  
14333
14334  
14335
14336  /*
14337  * - LGPL
14338  *
14339  * DateField
14340  * 
14341  */
14342
14343 /**
14344  * @class Roo.bootstrap.DateField
14345  * @extends Roo.bootstrap.Input
14346  * Bootstrap DateField class
14347  * @cfg {Number} weekStart default 0
14348  * @cfg {Number} weekStart default 0
14349  * @cfg {Number} viewMode default empty, (months|years)
14350  * @cfg {Number} minViewMode default empty, (months|years)
14351  * @cfg {Number} startDate default -Infinity
14352  * @cfg {Number} endDate default Infinity
14353  * @cfg {Boolean} todayHighlight default false
14354  * @cfg {Boolean} todayBtn default false
14355  * @cfg {Boolean} calendarWeeks default false
14356  * @cfg {Object} daysOfWeekDisabled default empty
14357  * 
14358  * @cfg {Boolean} keyboardNavigation default true
14359  * @cfg {String} language default en
14360  * 
14361  * @constructor
14362  * Create a new DateField
14363  * @param {Object} config The config object
14364  */
14365
14366 Roo.bootstrap.DateField = function(config){
14367     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14368      this.addEvents({
14369             /**
14370              * @event show
14371              * Fires when this field show.
14372              * @param {Roo.bootstrap.DateField} this
14373              * @param {Mixed} date The date value
14374              */
14375             show : true,
14376             /**
14377              * @event show
14378              * Fires when this field hide.
14379              * @param {Roo.bootstrap.DateField} this
14380              * @param {Mixed} date The date value
14381              */
14382             hide : true,
14383             /**
14384              * @event select
14385              * Fires when select a date.
14386              * @param {Roo.bootstrap.DateField} this
14387              * @param {Mixed} date The date value
14388              */
14389             select : true
14390         });
14391 };
14392
14393 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14394     
14395     /**
14396      * @cfg {String} format
14397      * The default date format string which can be overriden for localization support.  The format must be
14398      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14399      */
14400     format : "m/d/y",
14401     /**
14402      * @cfg {String} altFormats
14403      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14404      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14405      */
14406     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14407     
14408     weekStart : 0,
14409     
14410     viewMode : '',
14411     
14412     minViewMode : '',
14413     
14414     todayHighlight : false,
14415     
14416     todayBtn: false,
14417     
14418     language: 'en',
14419     
14420     keyboardNavigation: true,
14421     
14422     calendarWeeks: false,
14423     
14424     startDate: -Infinity,
14425     
14426     endDate: Infinity,
14427     
14428     daysOfWeekDisabled: [],
14429     
14430     _events: [],
14431     
14432     UTCDate: function()
14433     {
14434         return new Date(Date.UTC.apply(Date, arguments));
14435     },
14436     
14437     UTCToday: function()
14438     {
14439         var today = new Date();
14440         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14441     },
14442     
14443     getDate: function() {
14444             var d = this.getUTCDate();
14445             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14446     },
14447     
14448     getUTCDate: function() {
14449             return this.date;
14450     },
14451     
14452     setDate: function(d) {
14453             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14454     },
14455     
14456     setUTCDate: function(d) {
14457             this.date = d;
14458             this.setValue(this.formatDate(this.date));
14459     },
14460         
14461     onRender: function(ct, position)
14462     {
14463         
14464         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14465         
14466         this.language = this.language || 'en';
14467         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14468         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14469         
14470         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14471         this.format = this.format || 'm/d/y';
14472         this.isInline = false;
14473         this.isInput = true;
14474         this.component = this.el.select('.add-on', true).first() || false;
14475         this.component = (this.component && this.component.length === 0) ? false : this.component;
14476         this.hasInput = this.component && this.inputEL().length;
14477         
14478         if (typeof(this.minViewMode === 'string')) {
14479             switch (this.minViewMode) {
14480                 case 'months':
14481                     this.minViewMode = 1;
14482                     break;
14483                 case 'years':
14484                     this.minViewMode = 2;
14485                     break;
14486                 default:
14487                     this.minViewMode = 0;
14488                     break;
14489             }
14490         }
14491         
14492         if (typeof(this.viewMode === 'string')) {
14493             switch (this.viewMode) {
14494                 case 'months':
14495                     this.viewMode = 1;
14496                     break;
14497                 case 'years':
14498                     this.viewMode = 2;
14499                     break;
14500                 default:
14501                     this.viewMode = 0;
14502                     break;
14503             }
14504         }
14505                 
14506         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14507         
14508 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14509         
14510         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14511         
14512         this.picker().on('mousedown', this.onMousedown, this);
14513         this.picker().on('click', this.onClick, this);
14514         
14515         this.picker().addClass('datepicker-dropdown');
14516         
14517         this.startViewMode = this.viewMode;
14518         
14519         
14520         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14521             if(!this.calendarWeeks){
14522                 v.remove();
14523                 return;
14524             };
14525             
14526             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14527             v.attr('colspan', function(i, val){
14528                 return parseInt(val) + 1;
14529             });
14530         })
14531                         
14532         
14533         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14534         
14535         this.setStartDate(this.startDate);
14536         this.setEndDate(this.endDate);
14537         
14538         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14539         
14540         this.fillDow();
14541         this.fillMonths();
14542         this.update();
14543         this.showMode();
14544         
14545         if(this.isInline) {
14546             this.show();
14547         }
14548     },
14549     
14550     picker : function()
14551     {
14552         return this.pickerEl;
14553 //        return this.el.select('.datepicker', true).first();
14554     },
14555     
14556     fillDow: function()
14557     {
14558         var dowCnt = this.weekStart;
14559         
14560         var dow = {
14561             tag: 'tr',
14562             cn: [
14563                 
14564             ]
14565         };
14566         
14567         if(this.calendarWeeks){
14568             dow.cn.push({
14569                 tag: 'th',
14570                 cls: 'cw',
14571                 html: '&nbsp;'
14572             })
14573         }
14574         
14575         while (dowCnt < this.weekStart + 7) {
14576             dow.cn.push({
14577                 tag: 'th',
14578                 cls: 'dow',
14579                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14580             });
14581         }
14582         
14583         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14584     },
14585     
14586     fillMonths: function()
14587     {    
14588         var i = 0
14589         var months = this.picker().select('>.datepicker-months td', true).first();
14590         
14591         months.dom.innerHTML = '';
14592         
14593         while (i < 12) {
14594             var month = {
14595                 tag: 'span',
14596                 cls: 'month',
14597                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14598             }
14599             
14600             months.createChild(month);
14601         }
14602         
14603     },
14604     
14605     update: function()
14606     {
14607         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;
14608         
14609         if (this.date < this.startDate) {
14610             this.viewDate = new Date(this.startDate);
14611         } else if (this.date > this.endDate) {
14612             this.viewDate = new Date(this.endDate);
14613         } else {
14614             this.viewDate = new Date(this.date);
14615         }
14616         
14617         this.fill();
14618     },
14619     
14620     fill: function() 
14621     {
14622         var d = new Date(this.viewDate),
14623                 year = d.getUTCFullYear(),
14624                 month = d.getUTCMonth(),
14625                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14626                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14627                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14628                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14629                 currentDate = this.date && this.date.valueOf(),
14630                 today = this.UTCToday();
14631         
14632         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14633         
14634 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14635         
14636 //        this.picker.select('>tfoot th.today').
14637 //                                              .text(dates[this.language].today)
14638 //                                              .toggle(this.todayBtn !== false);
14639     
14640         this.updateNavArrows();
14641         this.fillMonths();
14642                                                 
14643         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14644         
14645         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14646          
14647         prevMonth.setUTCDate(day);
14648         
14649         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14650         
14651         var nextMonth = new Date(prevMonth);
14652         
14653         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14654         
14655         nextMonth = nextMonth.valueOf();
14656         
14657         var fillMonths = false;
14658         
14659         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14660         
14661         while(prevMonth.valueOf() < nextMonth) {
14662             var clsName = '';
14663             
14664             if (prevMonth.getUTCDay() === this.weekStart) {
14665                 if(fillMonths){
14666                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14667                 }
14668                     
14669                 fillMonths = {
14670                     tag: 'tr',
14671                     cn: []
14672                 };
14673                 
14674                 if(this.calendarWeeks){
14675                     // ISO 8601: First week contains first thursday.
14676                     // ISO also states week starts on Monday, but we can be more abstract here.
14677                     var
14678                     // Start of current week: based on weekstart/current date
14679                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14680                     // Thursday of this week
14681                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14682                     // First Thursday of year, year from thursday
14683                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14684                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14685                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14686                     
14687                     fillMonths.cn.push({
14688                         tag: 'td',
14689                         cls: 'cw',
14690                         html: calWeek
14691                     });
14692                 }
14693             }
14694             
14695             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14696                 clsName += ' old';
14697             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14698                 clsName += ' new';
14699             }
14700             if (this.todayHighlight &&
14701                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14702                 prevMonth.getUTCMonth() == today.getMonth() &&
14703                 prevMonth.getUTCDate() == today.getDate()) {
14704                 clsName += ' today';
14705             }
14706             
14707             if (currentDate && prevMonth.valueOf() === currentDate) {
14708                 clsName += ' active';
14709             }
14710             
14711             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14712                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14713                     clsName += ' disabled';
14714             }
14715             
14716             fillMonths.cn.push({
14717                 tag: 'td',
14718                 cls: 'day ' + clsName,
14719                 html: prevMonth.getDate()
14720             })
14721             
14722             prevMonth.setDate(prevMonth.getDate()+1);
14723         }
14724           
14725         var currentYear = this.date && this.date.getUTCFullYear();
14726         var currentMonth = this.date && this.date.getUTCMonth();
14727         
14728         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14729         
14730         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14731             v.removeClass('active');
14732             
14733             if(currentYear === year && k === currentMonth){
14734                 v.addClass('active');
14735             }
14736             
14737             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14738                 v.addClass('disabled');
14739             }
14740             
14741         });
14742         
14743         
14744         year = parseInt(year/10, 10) * 10;
14745         
14746         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14747         
14748         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14749         
14750         year -= 1;
14751         for (var i = -1; i < 11; i++) {
14752             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14753                 tag: 'span',
14754                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14755                 html: year
14756             })
14757             
14758             year += 1;
14759         }
14760     },
14761     
14762     showMode: function(dir) 
14763     {
14764         if (dir) {
14765             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14766         }
14767         Roo.each(this.picker().select('>div',true).elements, function(v){
14768             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14769             v.hide();
14770         });
14771         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14772     },
14773     
14774     place: function()
14775     {
14776         if(this.isInline) return;
14777         
14778         this.picker().removeClass(['bottom', 'top']);
14779         
14780         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14781             /*
14782              * place to the top of element!
14783              *
14784              */
14785             
14786             this.picker().addClass('top');
14787             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14788             
14789             return;
14790         }
14791         
14792         this.picker().addClass('bottom');
14793         
14794         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14795     },
14796     
14797     parseDate : function(value)
14798     {
14799         if(!value || value instanceof Date){
14800             return value;
14801         }
14802         var v = Date.parseDate(value, this.format);
14803         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14804             v = Date.parseDate(value, 'Y-m-d');
14805         }
14806         if(!v && this.altFormats){
14807             if(!this.altFormatsArray){
14808                 this.altFormatsArray = this.altFormats.split("|");
14809             }
14810             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14811                 v = Date.parseDate(value, this.altFormatsArray[i]);
14812             }
14813         }
14814         return v;
14815     },
14816     
14817     formatDate : function(date, fmt)
14818     {
14819         return (!date || !(date instanceof Date)) ?
14820         date : date.dateFormat(fmt || this.format);
14821     },
14822     
14823     onFocus : function()
14824     {
14825         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14826         this.show();
14827     },
14828     
14829     onBlur : function()
14830     {
14831         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14832         
14833         var d = this.inputEl().getValue();
14834         
14835         this.setValue(d);
14836                 
14837         this.hide();
14838     },
14839     
14840     show : function()
14841     {
14842         this.picker().show();
14843         this.update();
14844         this.place();
14845         
14846         this.fireEvent('show', this, this.date);
14847     },
14848     
14849     hide : function()
14850     {
14851         if(this.isInline) return;
14852         this.picker().hide();
14853         this.viewMode = this.startViewMode;
14854         this.showMode();
14855         
14856         this.fireEvent('hide', this, this.date);
14857         
14858     },
14859     
14860     onMousedown: function(e)
14861     {
14862         e.stopPropagation();
14863         e.preventDefault();
14864     },
14865     
14866     keyup: function(e)
14867     {
14868         Roo.bootstrap.DateField.superclass.keyup.call(this);
14869         this.update();
14870     },
14871
14872     setValue: function(v)
14873     {
14874         
14875         // v can be a string or a date..
14876         
14877         
14878         var d = new Date(this.parseDate(v) ).clearTime();
14879         
14880         Roo.log(d);
14881         Roo.log(d);
14882         if(isNaN(d.getTime())){
14883             this.date = this.viewDate = '';
14884             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14885             return;
14886         }
14887         
14888         v = this.formatDate(d);
14889         
14890         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14891         
14892         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14893      
14894         this.update();
14895
14896         this.fireEvent('select', this, this.date);
14897         
14898     },
14899     
14900     getValue: function()
14901     {
14902         return this.formatDate(this.date);
14903     },
14904     
14905     fireKey: function(e)
14906     {
14907         if (!this.picker().isVisible()){
14908             if (e.keyCode == 27) // allow escape to hide and re-show picker
14909                 this.show();
14910             return;
14911         }
14912         
14913         var dateChanged = false,
14914         dir, day, month,
14915         newDate, newViewDate;
14916         
14917         switch(e.keyCode){
14918             case 27: // escape
14919                 this.hide();
14920                 e.preventDefault();
14921                 break;
14922             case 37: // left
14923             case 39: // right
14924                 if (!this.keyboardNavigation) break;
14925                 dir = e.keyCode == 37 ? -1 : 1;
14926                 
14927                 if (e.ctrlKey){
14928                     newDate = this.moveYear(this.date, dir);
14929                     newViewDate = this.moveYear(this.viewDate, dir);
14930                 } else if (e.shiftKey){
14931                     newDate = this.moveMonth(this.date, dir);
14932                     newViewDate = this.moveMonth(this.viewDate, dir);
14933                 } else {
14934                     newDate = new Date(this.date);
14935                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14936                     newViewDate = new Date(this.viewDate);
14937                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14938                 }
14939                 if (this.dateWithinRange(newDate)){
14940                     this.date = newDate;
14941                     this.viewDate = newViewDate;
14942                     this.setValue(this.formatDate(this.date));
14943 //                    this.update();
14944                     e.preventDefault();
14945                     dateChanged = true;
14946                 }
14947                 break;
14948             case 38: // up
14949             case 40: // down
14950                 if (!this.keyboardNavigation) break;
14951                 dir = e.keyCode == 38 ? -1 : 1;
14952                 if (e.ctrlKey){
14953                     newDate = this.moveYear(this.date, dir);
14954                     newViewDate = this.moveYear(this.viewDate, dir);
14955                 } else if (e.shiftKey){
14956                     newDate = this.moveMonth(this.date, dir);
14957                     newViewDate = this.moveMonth(this.viewDate, dir);
14958                 } else {
14959                     newDate = new Date(this.date);
14960                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14961                     newViewDate = new Date(this.viewDate);
14962                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14963                 }
14964                 if (this.dateWithinRange(newDate)){
14965                     this.date = newDate;
14966                     this.viewDate = newViewDate;
14967                     this.setValue(this.formatDate(this.date));
14968 //                    this.update();
14969                     e.preventDefault();
14970                     dateChanged = true;
14971                 }
14972                 break;
14973             case 13: // enter
14974                 this.setValue(this.formatDate(this.date));
14975                 this.hide();
14976                 e.preventDefault();
14977                 break;
14978             case 9: // tab
14979                 this.setValue(this.formatDate(this.date));
14980                 this.hide();
14981                 break;
14982             case 16: // shift
14983             case 17: // ctrl
14984             case 18: // alt
14985                 break;
14986             default :
14987                 this.hide();
14988                 
14989         }
14990     },
14991     
14992     
14993     onClick: function(e) 
14994     {
14995         e.stopPropagation();
14996         e.preventDefault();
14997         
14998         var target = e.getTarget();
14999         
15000         if(target.nodeName.toLowerCase() === 'i'){
15001             target = Roo.get(target).dom.parentNode;
15002         }
15003         
15004         var nodeName = target.nodeName;
15005         var className = target.className;
15006         var html = target.innerHTML;
15007         //Roo.log(nodeName);
15008         
15009         switch(nodeName.toLowerCase()) {
15010             case 'th':
15011                 switch(className) {
15012                     case 'switch':
15013                         this.showMode(1);
15014                         break;
15015                     case 'prev':
15016                     case 'next':
15017                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15018                         switch(this.viewMode){
15019                                 case 0:
15020                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15021                                         break;
15022                                 case 1:
15023                                 case 2:
15024                                         this.viewDate = this.moveYear(this.viewDate, dir);
15025                                         break;
15026                         }
15027                         this.fill();
15028                         break;
15029                     case 'today':
15030                         var date = new Date();
15031                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15032 //                        this.fill()
15033                         this.setValue(this.formatDate(this.date));
15034                         
15035                         this.hide();
15036                         break;
15037                 }
15038                 break;
15039             case 'span':
15040                 if (className.indexOf('disabled') < 0) {
15041                     this.viewDate.setUTCDate(1);
15042                     if (className.indexOf('month') > -1) {
15043                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15044                     } else {
15045                         var year = parseInt(html, 10) || 0;
15046                         this.viewDate.setUTCFullYear(year);
15047                         
15048                     }
15049                     this.showMode(-1);
15050                     this.fill();
15051                 }
15052                 break;
15053                 
15054             case 'td':
15055                 //Roo.log(className);
15056                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15057                     var day = parseInt(html, 10) || 1;
15058                     var year = this.viewDate.getUTCFullYear(),
15059                         month = this.viewDate.getUTCMonth();
15060
15061                     if (className.indexOf('old') > -1) {
15062                         if(month === 0 ){
15063                             month = 11;
15064                             year -= 1;
15065                         }else{
15066                             month -= 1;
15067                         }
15068                     } else if (className.indexOf('new') > -1) {
15069                         if (month == 11) {
15070                             month = 0;
15071                             year += 1;
15072                         } else {
15073                             month += 1;
15074                         }
15075                     }
15076                     //Roo.log([year,month,day]);
15077                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15078                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15079 //                    this.fill();
15080                     //Roo.log(this.formatDate(this.date));
15081                     this.setValue(this.formatDate(this.date));
15082                     this.hide();
15083                 }
15084                 break;
15085         }
15086     },
15087     
15088     setStartDate: function(startDate)
15089     {
15090         this.startDate = startDate || -Infinity;
15091         if (this.startDate !== -Infinity) {
15092             this.startDate = this.parseDate(this.startDate);
15093         }
15094         this.update();
15095         this.updateNavArrows();
15096     },
15097
15098     setEndDate: function(endDate)
15099     {
15100         this.endDate = endDate || Infinity;
15101         if (this.endDate !== Infinity) {
15102             this.endDate = this.parseDate(this.endDate);
15103         }
15104         this.update();
15105         this.updateNavArrows();
15106     },
15107     
15108     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15109     {
15110         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15111         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15112             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15113         }
15114         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15115             return parseInt(d, 10);
15116         });
15117         this.update();
15118         this.updateNavArrows();
15119     },
15120     
15121     updateNavArrows: function() 
15122     {
15123         var d = new Date(this.viewDate),
15124         year = d.getUTCFullYear(),
15125         month = d.getUTCMonth();
15126         
15127         Roo.each(this.picker().select('.prev', true).elements, function(v){
15128             v.show();
15129             switch (this.viewMode) {
15130                 case 0:
15131
15132                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15133                         v.hide();
15134                     }
15135                     break;
15136                 case 1:
15137                 case 2:
15138                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15139                         v.hide();
15140                     }
15141                     break;
15142             }
15143         });
15144         
15145         Roo.each(this.picker().select('.next', true).elements, function(v){
15146             v.show();
15147             switch (this.viewMode) {
15148                 case 0:
15149
15150                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15151                         v.hide();
15152                     }
15153                     break;
15154                 case 1:
15155                 case 2:
15156                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15157                         v.hide();
15158                     }
15159                     break;
15160             }
15161         })
15162     },
15163     
15164     moveMonth: function(date, dir)
15165     {
15166         if (!dir) return date;
15167         var new_date = new Date(date.valueOf()),
15168         day = new_date.getUTCDate(),
15169         month = new_date.getUTCMonth(),
15170         mag = Math.abs(dir),
15171         new_month, test;
15172         dir = dir > 0 ? 1 : -1;
15173         if (mag == 1){
15174             test = dir == -1
15175             // If going back one month, make sure month is not current month
15176             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15177             ? function(){
15178                 return new_date.getUTCMonth() == month;
15179             }
15180             // If going forward one month, make sure month is as expected
15181             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15182             : function(){
15183                 return new_date.getUTCMonth() != new_month;
15184             };
15185             new_month = month + dir;
15186             new_date.setUTCMonth(new_month);
15187             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15188             if (new_month < 0 || new_month > 11)
15189                 new_month = (new_month + 12) % 12;
15190         } else {
15191             // For magnitudes >1, move one month at a time...
15192             for (var i=0; i<mag; i++)
15193                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15194                 new_date = this.moveMonth(new_date, dir);
15195             // ...then reset the day, keeping it in the new month
15196             new_month = new_date.getUTCMonth();
15197             new_date.setUTCDate(day);
15198             test = function(){
15199                 return new_month != new_date.getUTCMonth();
15200             };
15201         }
15202         // Common date-resetting loop -- if date is beyond end of month, make it
15203         // end of month
15204         while (test()){
15205             new_date.setUTCDate(--day);
15206             new_date.setUTCMonth(new_month);
15207         }
15208         return new_date;
15209     },
15210
15211     moveYear: function(date, dir)
15212     {
15213         return this.moveMonth(date, dir*12);
15214     },
15215
15216     dateWithinRange: function(date)
15217     {
15218         return date >= this.startDate && date <= this.endDate;
15219     },
15220
15221     
15222     remove: function() 
15223     {
15224         this.picker().remove();
15225     }
15226    
15227 });
15228
15229 Roo.apply(Roo.bootstrap.DateField,  {
15230     
15231     head : {
15232         tag: 'thead',
15233         cn: [
15234         {
15235             tag: 'tr',
15236             cn: [
15237             {
15238                 tag: 'th',
15239                 cls: 'prev',
15240                 html: '<i class="fa fa-arrow-left"/>'
15241             },
15242             {
15243                 tag: 'th',
15244                 cls: 'switch',
15245                 colspan: '5'
15246             },
15247             {
15248                 tag: 'th',
15249                 cls: 'next',
15250                 html: '<i class="fa fa-arrow-right"/>'
15251             }
15252
15253             ]
15254         }
15255         ]
15256     },
15257     
15258     content : {
15259         tag: 'tbody',
15260         cn: [
15261         {
15262             tag: 'tr',
15263             cn: [
15264             {
15265                 tag: 'td',
15266                 colspan: '7'
15267             }
15268             ]
15269         }
15270         ]
15271     },
15272     
15273     footer : {
15274         tag: 'tfoot',
15275         cn: [
15276         {
15277             tag: 'tr',
15278             cn: [
15279             {
15280                 tag: 'th',
15281                 colspan: '7',
15282                 cls: 'today'
15283             }
15284                     
15285             ]
15286         }
15287         ]
15288     },
15289     
15290     dates:{
15291         en: {
15292             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15293             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15294             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15295             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15296             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15297             today: "Today"
15298         }
15299     },
15300     
15301     modes: [
15302     {
15303         clsName: 'days',
15304         navFnc: 'Month',
15305         navStep: 1
15306     },
15307     {
15308         clsName: 'months',
15309         navFnc: 'FullYear',
15310         navStep: 1
15311     },
15312     {
15313         clsName: 'years',
15314         navFnc: 'FullYear',
15315         navStep: 10
15316     }]
15317 });
15318
15319 Roo.apply(Roo.bootstrap.DateField,  {
15320   
15321     template : {
15322         tag: 'div',
15323         cls: 'datepicker dropdown-menu',
15324         cn: [
15325         {
15326             tag: 'div',
15327             cls: 'datepicker-days',
15328             cn: [
15329             {
15330                 tag: 'table',
15331                 cls: 'table-condensed',
15332                 cn:[
15333                 Roo.bootstrap.DateField.head,
15334                 {
15335                     tag: 'tbody'
15336                 },
15337                 Roo.bootstrap.DateField.footer
15338                 ]
15339             }
15340             ]
15341         },
15342         {
15343             tag: 'div',
15344             cls: 'datepicker-months',
15345             cn: [
15346             {
15347                 tag: 'table',
15348                 cls: 'table-condensed',
15349                 cn:[
15350                 Roo.bootstrap.DateField.head,
15351                 Roo.bootstrap.DateField.content,
15352                 Roo.bootstrap.DateField.footer
15353                 ]
15354             }
15355             ]
15356         },
15357         {
15358             tag: 'div',
15359             cls: 'datepicker-years',
15360             cn: [
15361             {
15362                 tag: 'table',
15363                 cls: 'table-condensed',
15364                 cn:[
15365                 Roo.bootstrap.DateField.head,
15366                 Roo.bootstrap.DateField.content,
15367                 Roo.bootstrap.DateField.footer
15368                 ]
15369             }
15370             ]
15371         }
15372         ]
15373     }
15374 });
15375
15376  
15377
15378  /*
15379  * - LGPL
15380  *
15381  * TimeField
15382  * 
15383  */
15384
15385 /**
15386  * @class Roo.bootstrap.TimeField
15387  * @extends Roo.bootstrap.Input
15388  * Bootstrap DateField class
15389  * 
15390  * 
15391  * @constructor
15392  * Create a new TimeField
15393  * @param {Object} config The config object
15394  */
15395
15396 Roo.bootstrap.TimeField = function(config){
15397     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15398     this.addEvents({
15399             /**
15400              * @event show
15401              * Fires when this field show.
15402              * @param {Roo.bootstrap.DateField} this
15403              * @param {Mixed} date The date value
15404              */
15405             show : true,
15406             /**
15407              * @event show
15408              * Fires when this field hide.
15409              * @param {Roo.bootstrap.DateField} this
15410              * @param {Mixed} date The date value
15411              */
15412             hide : true,
15413             /**
15414              * @event select
15415              * Fires when select a date.
15416              * @param {Roo.bootstrap.DateField} this
15417              * @param {Mixed} date The date value
15418              */
15419             select : true
15420         });
15421 };
15422
15423 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15424     
15425     /**
15426      * @cfg {String} format
15427      * The default time format string which can be overriden for localization support.  The format must be
15428      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15429      */
15430     format : "H:i",
15431        
15432     onRender: function(ct, position)
15433     {
15434         
15435         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15436                 
15437         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15438         
15439         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15440         
15441         this.pop = this.picker().select('>.datepicker-time',true).first();
15442         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15443         
15444         this.picker().on('mousedown', this.onMousedown, this);
15445         this.picker().on('click', this.onClick, this);
15446         
15447         this.picker().addClass('datepicker-dropdown');
15448     
15449         this.fillTime();
15450         this.update();
15451             
15452         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15453         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15454         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15455         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15456         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15457         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15458
15459     },
15460     
15461     fireKey: function(e){
15462         if (!this.picker().isVisible()){
15463             if (e.keyCode == 27) // allow escape to hide and re-show picker
15464                 this.show();
15465             return;
15466         }
15467
15468         e.preventDefault();
15469         
15470         switch(e.keyCode){
15471             case 27: // escape
15472                 this.hide();
15473                 break;
15474             case 37: // left
15475             case 39: // right
15476                 this.onTogglePeriod();
15477                 break;
15478             case 38: // up
15479                 this.onIncrementMinutes();
15480                 break;
15481             case 40: // down
15482                 this.onDecrementMinutes();
15483                 break;
15484             case 13: // enter
15485             case 9: // tab
15486                 this.setTime();
15487                 break;
15488         }
15489     },
15490     
15491     onClick: function(e) {
15492         e.stopPropagation();
15493         e.preventDefault();
15494     },
15495     
15496     picker : function()
15497     {
15498         return this.el.select('.datepicker', true).first();
15499     },
15500     
15501     fillTime: function()
15502     {    
15503         var time = this.pop.select('tbody', true).first();
15504         
15505         time.dom.innerHTML = '';
15506         
15507         time.createChild({
15508             tag: 'tr',
15509             cn: [
15510                 {
15511                     tag: 'td',
15512                     cn: [
15513                         {
15514                             tag: 'a',
15515                             href: '#',
15516                             cls: 'btn',
15517                             cn: [
15518                                 {
15519                                     tag: 'span',
15520                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15521                                 }
15522                             ]
15523                         } 
15524                     ]
15525                 },
15526                 {
15527                     tag: 'td',
15528                     cls: 'separator'
15529                 },
15530                 {
15531                     tag: 'td',
15532                     cn: [
15533                         {
15534                             tag: 'a',
15535                             href: '#',
15536                             cls: 'btn',
15537                             cn: [
15538                                 {
15539                                     tag: 'span',
15540                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15541                                 }
15542                             ]
15543                         }
15544                     ]
15545                 },
15546                 {
15547                     tag: 'td',
15548                     cls: 'separator'
15549                 }
15550             ]
15551         });
15552         
15553         time.createChild({
15554             tag: 'tr',
15555             cn: [
15556                 {
15557                     tag: 'td',
15558                     cn: [
15559                         {
15560                             tag: 'span',
15561                             cls: 'timepicker-hour',
15562                             html: '00'
15563                         }  
15564                     ]
15565                 },
15566                 {
15567                     tag: 'td',
15568                     cls: 'separator',
15569                     html: ':'
15570                 },
15571                 {
15572                     tag: 'td',
15573                     cn: [
15574                         {
15575                             tag: 'span',
15576                             cls: 'timepicker-minute',
15577                             html: '00'
15578                         }  
15579                     ]
15580                 },
15581                 {
15582                     tag: 'td',
15583                     cls: 'separator'
15584                 },
15585                 {
15586                     tag: 'td',
15587                     cn: [
15588                         {
15589                             tag: 'button',
15590                             type: 'button',
15591                             cls: 'btn btn-primary period',
15592                             html: 'AM'
15593                             
15594                         }
15595                     ]
15596                 }
15597             ]
15598         });
15599         
15600         time.createChild({
15601             tag: 'tr',
15602             cn: [
15603                 {
15604                     tag: 'td',
15605                     cn: [
15606                         {
15607                             tag: 'a',
15608                             href: '#',
15609                             cls: 'btn',
15610                             cn: [
15611                                 {
15612                                     tag: 'span',
15613                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15614                                 }
15615                             ]
15616                         }
15617                     ]
15618                 },
15619                 {
15620                     tag: 'td',
15621                     cls: 'separator'
15622                 },
15623                 {
15624                     tag: 'td',
15625                     cn: [
15626                         {
15627                             tag: 'a',
15628                             href: '#',
15629                             cls: 'btn',
15630                             cn: [
15631                                 {
15632                                     tag: 'span',
15633                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15634                                 }
15635                             ]
15636                         }
15637                     ]
15638                 },
15639                 {
15640                     tag: 'td',
15641                     cls: 'separator'
15642                 }
15643             ]
15644         });
15645         
15646     },
15647     
15648     update: function()
15649     {
15650         
15651         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15652         
15653         this.fill();
15654     },
15655     
15656     fill: function() 
15657     {
15658         var hours = this.time.getHours();
15659         var minutes = this.time.getMinutes();
15660         var period = 'AM';
15661         
15662         if(hours > 11){
15663             period = 'PM';
15664         }
15665         
15666         if(hours == 0){
15667             hours = 12;
15668         }
15669         
15670         
15671         if(hours > 12){
15672             hours = hours - 12;
15673         }
15674         
15675         if(hours < 10){
15676             hours = '0' + hours;
15677         }
15678         
15679         if(minutes < 10){
15680             minutes = '0' + minutes;
15681         }
15682         
15683         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15684         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15685         this.pop.select('button', true).first().dom.innerHTML = period;
15686         
15687     },
15688     
15689     place: function()
15690     {   
15691         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15692         
15693         var cls = ['bottom'];
15694         
15695         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15696             cls.pop();
15697             cls.push('top');
15698         }
15699         
15700         cls.push('right');
15701         
15702         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15703             cls.pop();
15704             cls.push('left');
15705         }
15706         
15707         this.picker().addClass(cls.join('-'));
15708         
15709         var _this = this;
15710         
15711         Roo.each(cls, function(c){
15712             if(c == 'bottom'){
15713                 _this.picker().setTop(_this.inputEl().getHeight());
15714                 return;
15715             }
15716             if(c == 'top'){
15717                 _this.picker().setTop(0 - _this.picker().getHeight());
15718                 return;
15719             }
15720             
15721             if(c == 'left'){
15722                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15723                 return;
15724             }
15725             if(c == 'right'){
15726                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15727                 return;
15728             }
15729         });
15730         
15731     },
15732   
15733     onFocus : function()
15734     {
15735         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15736         this.show();
15737     },
15738     
15739     onBlur : function()
15740     {
15741         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15742         this.hide();
15743     },
15744     
15745     show : function()
15746     {
15747         this.picker().show();
15748         this.pop.show();
15749         this.update();
15750         this.place();
15751         
15752         this.fireEvent('show', this, this.date);
15753     },
15754     
15755     hide : function()
15756     {
15757         this.picker().hide();
15758         this.pop.hide();
15759         
15760         this.fireEvent('hide', this, this.date);
15761     },
15762     
15763     setTime : function()
15764     {
15765         this.hide();
15766         this.setValue(this.time.format(this.format));
15767         
15768         this.fireEvent('select', this, this.date);
15769         
15770         
15771     },
15772     
15773     onMousedown: function(e){
15774         e.stopPropagation();
15775         e.preventDefault();
15776     },
15777     
15778     onIncrementHours: function()
15779     {
15780         Roo.log('onIncrementHours');
15781         this.time = this.time.add(Date.HOUR, 1);
15782         this.update();
15783         
15784     },
15785     
15786     onDecrementHours: function()
15787     {
15788         Roo.log('onDecrementHours');
15789         this.time = this.time.add(Date.HOUR, -1);
15790         this.update();
15791     },
15792     
15793     onIncrementMinutes: function()
15794     {
15795         Roo.log('onIncrementMinutes');
15796         this.time = this.time.add(Date.MINUTE, 1);
15797         this.update();
15798     },
15799     
15800     onDecrementMinutes: function()
15801     {
15802         Roo.log('onDecrementMinutes');
15803         this.time = this.time.add(Date.MINUTE, -1);
15804         this.update();
15805     },
15806     
15807     onTogglePeriod: function()
15808     {
15809         Roo.log('onTogglePeriod');
15810         this.time = this.time.add(Date.HOUR, 12);
15811         this.update();
15812     }
15813     
15814    
15815 });
15816
15817 Roo.apply(Roo.bootstrap.TimeField,  {
15818     
15819     content : {
15820         tag: 'tbody',
15821         cn: [
15822             {
15823                 tag: 'tr',
15824                 cn: [
15825                 {
15826                     tag: 'td',
15827                     colspan: '7'
15828                 }
15829                 ]
15830             }
15831         ]
15832     },
15833     
15834     footer : {
15835         tag: 'tfoot',
15836         cn: [
15837             {
15838                 tag: 'tr',
15839                 cn: [
15840                 {
15841                     tag: 'th',
15842                     colspan: '7',
15843                     cls: '',
15844                     cn: [
15845                         {
15846                             tag: 'button',
15847                             cls: 'btn btn-info ok',
15848                             html: 'OK'
15849                         }
15850                     ]
15851                 }
15852
15853                 ]
15854             }
15855         ]
15856     }
15857 });
15858
15859 Roo.apply(Roo.bootstrap.TimeField,  {
15860   
15861     template : {
15862         tag: 'div',
15863         cls: 'datepicker dropdown-menu',
15864         cn: [
15865             {
15866                 tag: 'div',
15867                 cls: 'datepicker-time',
15868                 cn: [
15869                 {
15870                     tag: 'table',
15871                     cls: 'table-condensed',
15872                     cn:[
15873                     Roo.bootstrap.TimeField.content,
15874                     Roo.bootstrap.TimeField.footer
15875                     ]
15876                 }
15877                 ]
15878             }
15879         ]
15880     }
15881 });
15882
15883  
15884
15885  /*
15886  * - LGPL
15887  *
15888  * CheckBox
15889  * 
15890  */
15891
15892 /**
15893  * @class Roo.bootstrap.CheckBox
15894  * @extends Roo.bootstrap.Input
15895  * Bootstrap CheckBox class
15896  * 
15897  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15898  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15899  * @cfg {String} boxLabel The text that appears beside the checkbox
15900  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15901  * @cfg {Boolean} checked initnal the element
15902  * 
15903  * 
15904  * @constructor
15905  * Create a new CheckBox
15906  * @param {Object} config The config object
15907  */
15908
15909 Roo.bootstrap.CheckBox = function(config){
15910     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15911    
15912         this.addEvents({
15913             /**
15914             * @event check
15915             * Fires when the element is checked or unchecked.
15916             * @param {Roo.bootstrap.CheckBox} this This input
15917             * @param {Boolean} checked The new checked value
15918             */
15919            check : true
15920         });
15921 };
15922
15923 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15924     
15925     inputType: 'checkbox',
15926     inputValue: 1,
15927     valueOff: 0,
15928     boxLabel: false,
15929     checked: false,
15930     weight : false,
15931     
15932     getAutoCreate : function()
15933     {
15934         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15935         
15936         var id = Roo.id();
15937         
15938         var cfg = {};
15939         
15940         cfg.cls = 'form-group checkbox' //input-group
15941         
15942         
15943         
15944         
15945         var input =  {
15946             tag: 'input',
15947             id : id,
15948             type : this.inputType,
15949             value : (!this.checked) ? this.valueOff : this.inputValue,
15950             cls : 'roo-checkbox', //'form-box',
15951             placeholder : this.placeholder || ''
15952             
15953         };
15954         
15955         if (this.weight) { // Validity check?
15956             cfg.cls += " checkbox-" + this.weight;
15957         }
15958         
15959         if (this.disabled) {
15960             input.disabled=true;
15961         }
15962         
15963         if(this.checked){
15964             input.checked = this.checked;
15965         }
15966         
15967         if (this.name) {
15968             input.name = this.name;
15969         }
15970         
15971         if (this.size) {
15972             input.cls += ' input-' + this.size;
15973         }
15974         
15975         var settings=this;
15976         ['xs','sm','md','lg'].map(function(size){
15977             if (settings[size]) {
15978                 cfg.cls += ' col-' + size + '-' + settings[size];
15979             }
15980         });
15981         
15982        
15983         
15984         var inputblock = input;
15985         
15986         
15987         
15988         
15989         if (this.before || this.after) {
15990             
15991             inputblock = {
15992                 cls : 'input-group',
15993                 cn :  [] 
15994             };
15995             if (this.before) {
15996                 inputblock.cn.push({
15997                     tag :'span',
15998                     cls : 'input-group-addon',
15999                     html : this.before
16000                 });
16001             }
16002             inputblock.cn.push(input);
16003             if (this.after) {
16004                 inputblock.cn.push({
16005                     tag :'span',
16006                     cls : 'input-group-addon',
16007                     html : this.after
16008                 });
16009             }
16010             
16011         };
16012         
16013         if (align ==='left' && this.fieldLabel.length) {
16014                 Roo.log("left and has label");
16015                 cfg.cn = [
16016                     
16017                     {
16018                         tag: 'label',
16019                         'for' :  id,
16020                         cls : 'control-label col-md-' + this.labelWidth,
16021                         html : this.fieldLabel
16022                         
16023                     },
16024                     {
16025                         cls : "col-md-" + (12 - this.labelWidth), 
16026                         cn: [
16027                             inputblock
16028                         ]
16029                     }
16030                     
16031                 ];
16032         } else if ( this.fieldLabel.length) {
16033                 Roo.log(" label");
16034                 cfg.cn = [
16035                    
16036                     {
16037                         tag: this.boxLabel ? 'span' : 'label',
16038                         'for': id,
16039                         cls: 'control-label box-input-label',
16040                         //cls : 'input-group-addon',
16041                         html : this.fieldLabel
16042                         
16043                     },
16044                     
16045                     inputblock
16046                     
16047                 ];
16048
16049         } else {
16050             
16051                 Roo.log(" no label && no align");
16052                 cfg.cn = [  inputblock ] ;
16053                 
16054                 
16055         };
16056          if(this.boxLabel){
16057             cfg.cn.push( {
16058                 tag: 'label',
16059                 'for': id,
16060                 cls: 'box-label',
16061                 html: this.boxLabel
16062                 
16063             });
16064         }
16065         
16066         
16067        
16068         return cfg;
16069         
16070     },
16071     
16072     /**
16073      * return the real input element.
16074      */
16075     inputEl: function ()
16076     {
16077         return this.el.select('input.roo-checkbox',true).first();
16078     },
16079     
16080     label: function()
16081     {
16082         return this.el.select('label.control-label',true).first();
16083     },
16084     
16085     initEvents : function()
16086     {
16087 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16088         
16089         this.inputEl().on('click', this.onClick,  this);
16090         
16091     },
16092     
16093     onClick : function()
16094     {   
16095         this.setChecked(!this.checked);
16096     },
16097     
16098     setChecked : function(state,suppressEvent)
16099     {
16100         this.checked = state;
16101         
16102         this.inputEl().dom.checked = state;
16103         
16104         if(suppressEvent !== true){
16105             this.fireEvent('check', this, state);
16106         }
16107         
16108         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16109         
16110     },
16111     
16112     setValue : function(v,suppressEvent)
16113     {
16114         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16115     }
16116     
16117 });
16118
16119  
16120 /*
16121  * - LGPL
16122  *
16123  * Radio
16124  * 
16125  */
16126
16127 /**
16128  * @class Roo.bootstrap.Radio
16129  * @extends Roo.bootstrap.CheckBox
16130  * Bootstrap Radio class
16131
16132  * @constructor
16133  * Create a new Radio
16134  * @param {Object} config The config object
16135  */
16136
16137 Roo.bootstrap.Radio = function(config){
16138     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16139    
16140 };
16141
16142 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16143     
16144     inputType: 'radio',
16145     inputValue: '',
16146     valueOff: '',
16147     
16148     getAutoCreate : function()
16149     {
16150         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16151         
16152         var id = Roo.id();
16153         
16154         var cfg = {};
16155         
16156         cfg.cls = 'form-group radio' //input-group
16157         
16158         var input =  {
16159             tag: 'input',
16160             id : id,
16161             type : this.inputType,
16162             value : (!this.checked) ? this.valueOff : this.inputValue,
16163             cls : 'roo-radio',
16164             placeholder : this.placeholder || ''
16165             
16166         };
16167           if (this.weight) { // Validity check?
16168             cfg.cls += " radio-" + this.weight;
16169         }
16170         if (this.disabled) {
16171             input.disabled=true;
16172         }
16173         
16174         if(this.checked){
16175             input.checked = this.checked;
16176         }
16177         
16178         if (this.name) {
16179             input.name = this.name;
16180         }
16181         
16182         if (this.size) {
16183             input.cls += ' input-' + this.size;
16184         }
16185         
16186         var settings=this;
16187         ['xs','sm','md','lg'].map(function(size){
16188             if (settings[size]) {
16189                 cfg.cls += ' col-' + size + '-' + settings[size];
16190             }
16191         });
16192         
16193         var inputblock = input;
16194         
16195         if (this.before || this.after) {
16196             
16197             inputblock = {
16198                 cls : 'input-group',
16199                 cn :  [] 
16200             };
16201             if (this.before) {
16202                 inputblock.cn.push({
16203                     tag :'span',
16204                     cls : 'input-group-addon',
16205                     html : this.before
16206                 });
16207             }
16208             inputblock.cn.push(input);
16209             if (this.after) {
16210                 inputblock.cn.push({
16211                     tag :'span',
16212                     cls : 'input-group-addon',
16213                     html : this.after
16214                 });
16215             }
16216             
16217         };
16218         
16219         if (align ==='left' && this.fieldLabel.length) {
16220                 Roo.log("left and has label");
16221                 cfg.cn = [
16222                     
16223                     {
16224                         tag: 'label',
16225                         'for' :  id,
16226                         cls : 'control-label col-md-' + this.labelWidth,
16227                         html : this.fieldLabel
16228                         
16229                     },
16230                     {
16231                         cls : "col-md-" + (12 - this.labelWidth), 
16232                         cn: [
16233                             inputblock
16234                         ]
16235                     }
16236                     
16237                 ];
16238         } else if ( this.fieldLabel.length) {
16239                 Roo.log(" label");
16240                  cfg.cn = [
16241                    
16242                     {
16243                         tag: 'label',
16244                         'for': id,
16245                         cls: 'control-label box-input-label',
16246                         //cls : 'input-group-addon',
16247                         html : this.fieldLabel
16248                         
16249                     },
16250                     
16251                     inputblock
16252                     
16253                 ];
16254
16255         } else {
16256             
16257                    Roo.log(" no label && no align");
16258                 cfg.cn = [
16259                     
16260                         inputblock
16261                     
16262                 ];
16263                 
16264                 
16265         };
16266         
16267         if(this.boxLabel){
16268             cfg.cn.push({
16269                 tag: 'label',
16270                 'for': id,
16271                 cls: 'box-label',
16272                 html: this.boxLabel
16273             })
16274         }
16275         
16276         return cfg;
16277         
16278     },
16279     inputEl: function ()
16280     {
16281         return this.el.select('input.roo-radio',true).first();
16282     },
16283     onClick : function()
16284     {   
16285         this.setChecked(true);
16286     },
16287     
16288     setChecked : function(state,suppressEvent)
16289     {
16290         if(state){
16291             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16292                 v.dom.checked = false;
16293             });
16294         }
16295         
16296         this.checked = state;
16297         this.inputEl().dom.checked = state;
16298         
16299         if(suppressEvent !== true){
16300             this.fireEvent('check', this, state);
16301         }
16302         
16303         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16304         
16305     },
16306     
16307     getGroupValue : function()
16308     {
16309         var value = ''
16310         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16311             if(v.dom.checked == true){
16312                 value = v.dom.value;
16313             }
16314         });
16315         
16316         return value;
16317     },
16318     
16319     /**
16320      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16321      * @return {Mixed} value The field value
16322      */
16323     getValue : function(){
16324         return this.getGroupValue();
16325     }
16326     
16327 });
16328
16329  
16330 //<script type="text/javascript">
16331
16332 /*
16333  * Based  Ext JS Library 1.1.1
16334  * Copyright(c) 2006-2007, Ext JS, LLC.
16335  * LGPL
16336  *
16337  */
16338  
16339 /**
16340  * @class Roo.HtmlEditorCore
16341  * @extends Roo.Component
16342  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16343  *
16344  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16345  */
16346
16347 Roo.HtmlEditorCore = function(config){
16348     
16349     
16350     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16351     
16352     
16353     this.addEvents({
16354         /**
16355          * @event initialize
16356          * Fires when the editor is fully initialized (including the iframe)
16357          * @param {Roo.HtmlEditorCore} this
16358          */
16359         initialize: true,
16360         /**
16361          * @event activate
16362          * Fires when the editor is first receives the focus. Any insertion must wait
16363          * until after this event.
16364          * @param {Roo.HtmlEditorCore} this
16365          */
16366         activate: true,
16367          /**
16368          * @event beforesync
16369          * Fires before the textarea is updated with content from the editor iframe. Return false
16370          * to cancel the sync.
16371          * @param {Roo.HtmlEditorCore} this
16372          * @param {String} html
16373          */
16374         beforesync: true,
16375          /**
16376          * @event beforepush
16377          * Fires before the iframe editor is updated with content from the textarea. Return false
16378          * to cancel the push.
16379          * @param {Roo.HtmlEditorCore} this
16380          * @param {String} html
16381          */
16382         beforepush: true,
16383          /**
16384          * @event sync
16385          * Fires when the textarea is updated with content from the editor iframe.
16386          * @param {Roo.HtmlEditorCore} this
16387          * @param {String} html
16388          */
16389         sync: true,
16390          /**
16391          * @event push
16392          * Fires when the iframe editor is updated with content from the textarea.
16393          * @param {Roo.HtmlEditorCore} this
16394          * @param {String} html
16395          */
16396         push: true,
16397         
16398         /**
16399          * @event editorevent
16400          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16401          * @param {Roo.HtmlEditorCore} this
16402          */
16403         editorevent: true
16404     });
16405     
16406     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16407     
16408     // defaults : white / black...
16409     this.applyBlacklists();
16410     
16411     
16412     
16413 };
16414
16415
16416 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16417
16418
16419      /**
16420      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16421      */
16422     
16423     owner : false,
16424     
16425      /**
16426      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16427      *                        Roo.resizable.
16428      */
16429     resizable : false,
16430      /**
16431      * @cfg {Number} height (in pixels)
16432      */   
16433     height: 300,
16434    /**
16435      * @cfg {Number} width (in pixels)
16436      */   
16437     width: 500,
16438     
16439     /**
16440      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16441      * 
16442      */
16443     stylesheets: false,
16444     
16445     // id of frame..
16446     frameId: false,
16447     
16448     // private properties
16449     validationEvent : false,
16450     deferHeight: true,
16451     initialized : false,
16452     activated : false,
16453     sourceEditMode : false,
16454     onFocus : Roo.emptyFn,
16455     iframePad:3,
16456     hideMode:'offsets',
16457     
16458     clearUp: true,
16459     
16460     // blacklist + whitelisted elements..
16461     black: false,
16462     white: false,
16463      
16464     
16465
16466     /**
16467      * Protected method that will not generally be called directly. It
16468      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16469      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16470      */
16471     getDocMarkup : function(){
16472         // body styles..
16473         var st = '';
16474         Roo.log(this.stylesheets);
16475         
16476         // inherit styels from page...?? 
16477         if (this.stylesheets === false) {
16478             
16479             Roo.get(document.head).select('style').each(function(node) {
16480                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16481             });
16482             
16483             Roo.get(document.head).select('link').each(function(node) { 
16484                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16485             });
16486             
16487         } else if (!this.stylesheets.length) {
16488                 // simple..
16489                 st = '<style type="text/css">' +
16490                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16491                    '</style>';
16492         } else {
16493             Roo.each(this.stylesheets, function(s) {
16494                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16495             });
16496             
16497         }
16498         
16499         st +=  '<style type="text/css">' +
16500             'IMG { cursor: pointer } ' +
16501         '</style>';
16502
16503         
16504         return '<html><head>' + st  +
16505             //<style type="text/css">' +
16506             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16507             //'</style>' +
16508             ' </head><body class="roo-htmleditor-body"></body></html>';
16509     },
16510
16511     // private
16512     onRender : function(ct, position)
16513     {
16514         var _t = this;
16515         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16516         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16517         
16518         
16519         this.el.dom.style.border = '0 none';
16520         this.el.dom.setAttribute('tabIndex', -1);
16521         this.el.addClass('x-hidden hide');
16522         
16523         
16524         
16525         if(Roo.isIE){ // fix IE 1px bogus margin
16526             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16527         }
16528        
16529         
16530         this.frameId = Roo.id();
16531         
16532          
16533         
16534         var iframe = this.owner.wrap.createChild({
16535             tag: 'iframe',
16536             cls: 'form-control', // bootstrap..
16537             id: this.frameId,
16538             name: this.frameId,
16539             frameBorder : 'no',
16540             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16541         }, this.el
16542         );
16543         
16544         
16545         this.iframe = iframe.dom;
16546
16547          this.assignDocWin();
16548         
16549         this.doc.designMode = 'on';
16550        
16551         this.doc.open();
16552         this.doc.write(this.getDocMarkup());
16553         this.doc.close();
16554
16555         
16556         var task = { // must defer to wait for browser to be ready
16557             run : function(){
16558                 //console.log("run task?" + this.doc.readyState);
16559                 this.assignDocWin();
16560                 if(this.doc.body || this.doc.readyState == 'complete'){
16561                     try {
16562                         this.doc.designMode="on";
16563                     } catch (e) {
16564                         return;
16565                     }
16566                     Roo.TaskMgr.stop(task);
16567                     this.initEditor.defer(10, this);
16568                 }
16569             },
16570             interval : 10,
16571             duration: 10000,
16572             scope: this
16573         };
16574         Roo.TaskMgr.start(task);
16575
16576         
16577          
16578     },
16579
16580     // private
16581     onResize : function(w, h)
16582     {
16583          Roo.log('resize: ' +w + ',' + h );
16584         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16585         if(!this.iframe){
16586             return;
16587         }
16588         if(typeof w == 'number'){
16589             
16590             this.iframe.style.width = w + 'px';
16591         }
16592         if(typeof h == 'number'){
16593             
16594             this.iframe.style.height = h + 'px';
16595             if(this.doc){
16596                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16597             }
16598         }
16599         
16600     },
16601
16602     /**
16603      * Toggles the editor between standard and source edit mode.
16604      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16605      */
16606     toggleSourceEdit : function(sourceEditMode){
16607         
16608         this.sourceEditMode = sourceEditMode === true;
16609         
16610         if(this.sourceEditMode){
16611  
16612             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16613             
16614         }else{
16615             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16616             //this.iframe.className = '';
16617             this.deferFocus();
16618         }
16619         //this.setSize(this.owner.wrap.getSize());
16620         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16621     },
16622
16623     
16624   
16625
16626     /**
16627      * Protected method that will not generally be called directly. If you need/want
16628      * custom HTML cleanup, this is the method you should override.
16629      * @param {String} html The HTML to be cleaned
16630      * return {String} The cleaned HTML
16631      */
16632     cleanHtml : function(html){
16633         html = String(html);
16634         if(html.length > 5){
16635             if(Roo.isSafari){ // strip safari nonsense
16636                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16637             }
16638         }
16639         if(html == '&nbsp;'){
16640             html = '';
16641         }
16642         return html;
16643     },
16644
16645     /**
16646      * HTML Editor -> Textarea
16647      * Protected method that will not generally be called directly. Syncs the contents
16648      * of the editor iframe with the textarea.
16649      */
16650     syncValue : function(){
16651         if(this.initialized){
16652             var bd = (this.doc.body || this.doc.documentElement);
16653             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16654             var html = bd.innerHTML;
16655             if(Roo.isSafari){
16656                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16657                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16658                 if(m && m[1]){
16659                     html = '<div style="'+m[0]+'">' + html + '</div>';
16660                 }
16661             }
16662             html = this.cleanHtml(html);
16663             // fix up the special chars.. normaly like back quotes in word...
16664             // however we do not want to do this with chinese..
16665             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16666                 var cc = b.charCodeAt();
16667                 if (
16668                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16669                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16670                     (cc >= 0xf900 && cc < 0xfb00 )
16671                 ) {
16672                         return b;
16673                 }
16674                 return "&#"+cc+";" 
16675             });
16676             if(this.owner.fireEvent('beforesync', this, html) !== false){
16677                 this.el.dom.value = html;
16678                 this.owner.fireEvent('sync', this, html);
16679             }
16680         }
16681     },
16682
16683     /**
16684      * Protected method that will not generally be called directly. Pushes the value of the textarea
16685      * into the iframe editor.
16686      */
16687     pushValue : function(){
16688         if(this.initialized){
16689             var v = this.el.dom.value.trim();
16690             
16691 //            if(v.length < 1){
16692 //                v = '&#160;';
16693 //            }
16694             
16695             if(this.owner.fireEvent('beforepush', this, v) !== false){
16696                 var d = (this.doc.body || this.doc.documentElement);
16697                 d.innerHTML = v;
16698                 this.cleanUpPaste();
16699                 this.el.dom.value = d.innerHTML;
16700                 this.owner.fireEvent('push', this, v);
16701             }
16702         }
16703     },
16704
16705     // private
16706     deferFocus : function(){
16707         this.focus.defer(10, this);
16708     },
16709
16710     // doc'ed in Field
16711     focus : function(){
16712         if(this.win && !this.sourceEditMode){
16713             this.win.focus();
16714         }else{
16715             this.el.focus();
16716         }
16717     },
16718     
16719     assignDocWin: function()
16720     {
16721         var iframe = this.iframe;
16722         
16723          if(Roo.isIE){
16724             this.doc = iframe.contentWindow.document;
16725             this.win = iframe.contentWindow;
16726         } else {
16727 //            if (!Roo.get(this.frameId)) {
16728 //                return;
16729 //            }
16730 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16731 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16732             
16733             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16734                 return;
16735             }
16736             
16737             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16738             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16739         }
16740     },
16741     
16742     // private
16743     initEditor : function(){
16744         //console.log("INIT EDITOR");
16745         this.assignDocWin();
16746         
16747         
16748         
16749         this.doc.designMode="on";
16750         this.doc.open();
16751         this.doc.write(this.getDocMarkup());
16752         this.doc.close();
16753         
16754         var dbody = (this.doc.body || this.doc.documentElement);
16755         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16756         // this copies styles from the containing element into thsi one..
16757         // not sure why we need all of this..
16758         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16759         
16760         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16761         //ss['background-attachment'] = 'fixed'; // w3c
16762         dbody.bgProperties = 'fixed'; // ie
16763         //Roo.DomHelper.applyStyles(dbody, ss);
16764         Roo.EventManager.on(this.doc, {
16765             //'mousedown': this.onEditorEvent,
16766             'mouseup': this.onEditorEvent,
16767             'dblclick': this.onEditorEvent,
16768             'click': this.onEditorEvent,
16769             'keyup': this.onEditorEvent,
16770             buffer:100,
16771             scope: this
16772         });
16773         if(Roo.isGecko){
16774             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16775         }
16776         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16777             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16778         }
16779         this.initialized = true;
16780
16781         this.owner.fireEvent('initialize', this);
16782         this.pushValue();
16783     },
16784
16785     // private
16786     onDestroy : function(){
16787         
16788         
16789         
16790         if(this.rendered){
16791             
16792             //for (var i =0; i < this.toolbars.length;i++) {
16793             //    // fixme - ask toolbars for heights?
16794             //    this.toolbars[i].onDestroy();
16795            // }
16796             
16797             //this.wrap.dom.innerHTML = '';
16798             //this.wrap.remove();
16799         }
16800     },
16801
16802     // private
16803     onFirstFocus : function(){
16804         
16805         this.assignDocWin();
16806         
16807         
16808         this.activated = true;
16809          
16810     
16811         if(Roo.isGecko){ // prevent silly gecko errors
16812             this.win.focus();
16813             var s = this.win.getSelection();
16814             if(!s.focusNode || s.focusNode.nodeType != 3){
16815                 var r = s.getRangeAt(0);
16816                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16817                 r.collapse(true);
16818                 this.deferFocus();
16819             }
16820             try{
16821                 this.execCmd('useCSS', true);
16822                 this.execCmd('styleWithCSS', false);
16823             }catch(e){}
16824         }
16825         this.owner.fireEvent('activate', this);
16826     },
16827
16828     // private
16829     adjustFont: function(btn){
16830         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16831         //if(Roo.isSafari){ // safari
16832         //    adjust *= 2;
16833        // }
16834         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16835         if(Roo.isSafari){ // safari
16836             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16837             v =  (v < 10) ? 10 : v;
16838             v =  (v > 48) ? 48 : v;
16839             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16840             
16841         }
16842         
16843         
16844         v = Math.max(1, v+adjust);
16845         
16846         this.execCmd('FontSize', v  );
16847     },
16848
16849     onEditorEvent : function(e){
16850         this.owner.fireEvent('editorevent', this, e);
16851       //  this.updateToolbar();
16852         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16853     },
16854
16855     insertTag : function(tg)
16856     {
16857         // could be a bit smarter... -> wrap the current selected tRoo..
16858         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16859             
16860             range = this.createRange(this.getSelection());
16861             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16862             wrappingNode.appendChild(range.extractContents());
16863             range.insertNode(wrappingNode);
16864
16865             return;
16866             
16867             
16868             
16869         }
16870         this.execCmd("formatblock",   tg);
16871         
16872     },
16873     
16874     insertText : function(txt)
16875     {
16876         
16877         
16878         var range = this.createRange();
16879         range.deleteContents();
16880                //alert(Sender.getAttribute('label'));
16881                
16882         range.insertNode(this.doc.createTextNode(txt));
16883     } ,
16884     
16885      
16886
16887     /**
16888      * Executes a Midas editor command on the editor document and performs necessary focus and
16889      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16890      * @param {String} cmd The Midas command
16891      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16892      */
16893     relayCmd : function(cmd, value){
16894         this.win.focus();
16895         this.execCmd(cmd, value);
16896         this.owner.fireEvent('editorevent', this);
16897         //this.updateToolbar();
16898         this.owner.deferFocus();
16899     },
16900
16901     /**
16902      * Executes a Midas editor command directly on the editor document.
16903      * For visual commands, you should use {@link #relayCmd} instead.
16904      * <b>This should only be called after the editor is initialized.</b>
16905      * @param {String} cmd The Midas command
16906      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16907      */
16908     execCmd : function(cmd, value){
16909         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16910         this.syncValue();
16911     },
16912  
16913  
16914    
16915     /**
16916      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16917      * to insert tRoo.
16918      * @param {String} text | dom node.. 
16919      */
16920     insertAtCursor : function(text)
16921     {
16922         
16923         
16924         
16925         if(!this.activated){
16926             return;
16927         }
16928         /*
16929         if(Roo.isIE){
16930             this.win.focus();
16931             var r = this.doc.selection.createRange();
16932             if(r){
16933                 r.collapse(true);
16934                 r.pasteHTML(text);
16935                 this.syncValue();
16936                 this.deferFocus();
16937             
16938             }
16939             return;
16940         }
16941         */
16942         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16943             this.win.focus();
16944             
16945             
16946             // from jquery ui (MIT licenced)
16947             var range, node;
16948             var win = this.win;
16949             
16950             if (win.getSelection && win.getSelection().getRangeAt) {
16951                 range = win.getSelection().getRangeAt(0);
16952                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16953                 range.insertNode(node);
16954             } else if (win.document.selection && win.document.selection.createRange) {
16955                 // no firefox support
16956                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16957                 win.document.selection.createRange().pasteHTML(txt);
16958             } else {
16959                 // no firefox support
16960                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16961                 this.execCmd('InsertHTML', txt);
16962             } 
16963             
16964             this.syncValue();
16965             
16966             this.deferFocus();
16967         }
16968     },
16969  // private
16970     mozKeyPress : function(e){
16971         if(e.ctrlKey){
16972             var c = e.getCharCode(), cmd;
16973           
16974             if(c > 0){
16975                 c = String.fromCharCode(c).toLowerCase();
16976                 switch(c){
16977                     case 'b':
16978                         cmd = 'bold';
16979                         break;
16980                     case 'i':
16981                         cmd = 'italic';
16982                         break;
16983                     
16984                     case 'u':
16985                         cmd = 'underline';
16986                         break;
16987                     
16988                     case 'v':
16989                         this.cleanUpPaste.defer(100, this);
16990                         return;
16991                         
16992                 }
16993                 if(cmd){
16994                     this.win.focus();
16995                     this.execCmd(cmd);
16996                     this.deferFocus();
16997                     e.preventDefault();
16998                 }
16999                 
17000             }
17001         }
17002     },
17003
17004     // private
17005     fixKeys : function(){ // load time branching for fastest keydown performance
17006         if(Roo.isIE){
17007             return function(e){
17008                 var k = e.getKey(), r;
17009                 if(k == e.TAB){
17010                     e.stopEvent();
17011                     r = this.doc.selection.createRange();
17012                     if(r){
17013                         r.collapse(true);
17014                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17015                         this.deferFocus();
17016                     }
17017                     return;
17018                 }
17019                 
17020                 if(k == e.ENTER){
17021                     r = this.doc.selection.createRange();
17022                     if(r){
17023                         var target = r.parentElement();
17024                         if(!target || target.tagName.toLowerCase() != 'li'){
17025                             e.stopEvent();
17026                             r.pasteHTML('<br />');
17027                             r.collapse(false);
17028                             r.select();
17029                         }
17030                     }
17031                 }
17032                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17033                     this.cleanUpPaste.defer(100, this);
17034                     return;
17035                 }
17036                 
17037                 
17038             };
17039         }else if(Roo.isOpera){
17040             return function(e){
17041                 var k = e.getKey();
17042                 if(k == e.TAB){
17043                     e.stopEvent();
17044                     this.win.focus();
17045                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17046                     this.deferFocus();
17047                 }
17048                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17049                     this.cleanUpPaste.defer(100, this);
17050                     return;
17051                 }
17052                 
17053             };
17054         }else if(Roo.isSafari){
17055             return function(e){
17056                 var k = e.getKey();
17057                 
17058                 if(k == e.TAB){
17059                     e.stopEvent();
17060                     this.execCmd('InsertText','\t');
17061                     this.deferFocus();
17062                     return;
17063                 }
17064                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17065                     this.cleanUpPaste.defer(100, this);
17066                     return;
17067                 }
17068                 
17069              };
17070         }
17071     }(),
17072     
17073     getAllAncestors: function()
17074     {
17075         var p = this.getSelectedNode();
17076         var a = [];
17077         if (!p) {
17078             a.push(p); // push blank onto stack..
17079             p = this.getParentElement();
17080         }
17081         
17082         
17083         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17084             a.push(p);
17085             p = p.parentNode;
17086         }
17087         a.push(this.doc.body);
17088         return a;
17089     },
17090     lastSel : false,
17091     lastSelNode : false,
17092     
17093     
17094     getSelection : function() 
17095     {
17096         this.assignDocWin();
17097         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17098     },
17099     
17100     getSelectedNode: function() 
17101     {
17102         // this may only work on Gecko!!!
17103         
17104         // should we cache this!!!!
17105         
17106         
17107         
17108          
17109         var range = this.createRange(this.getSelection()).cloneRange();
17110         
17111         if (Roo.isIE) {
17112             var parent = range.parentElement();
17113             while (true) {
17114                 var testRange = range.duplicate();
17115                 testRange.moveToElementText(parent);
17116                 if (testRange.inRange(range)) {
17117                     break;
17118                 }
17119                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17120                     break;
17121                 }
17122                 parent = parent.parentElement;
17123             }
17124             return parent;
17125         }
17126         
17127         // is ancestor a text element.
17128         var ac =  range.commonAncestorContainer;
17129         if (ac.nodeType == 3) {
17130             ac = ac.parentNode;
17131         }
17132         
17133         var ar = ac.childNodes;
17134          
17135         var nodes = [];
17136         var other_nodes = [];
17137         var has_other_nodes = false;
17138         for (var i=0;i<ar.length;i++) {
17139             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17140                 continue;
17141             }
17142             // fullly contained node.
17143             
17144             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17145                 nodes.push(ar[i]);
17146                 continue;
17147             }
17148             
17149             // probably selected..
17150             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17151                 other_nodes.push(ar[i]);
17152                 continue;
17153             }
17154             // outer..
17155             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17156                 continue;
17157             }
17158             
17159             
17160             has_other_nodes = true;
17161         }
17162         if (!nodes.length && other_nodes.length) {
17163             nodes= other_nodes;
17164         }
17165         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17166             return false;
17167         }
17168         
17169         return nodes[0];
17170     },
17171     createRange: function(sel)
17172     {
17173         // this has strange effects when using with 
17174         // top toolbar - not sure if it's a great idea.
17175         //this.editor.contentWindow.focus();
17176         if (typeof sel != "undefined") {
17177             try {
17178                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17179             } catch(e) {
17180                 return this.doc.createRange();
17181             }
17182         } else {
17183             return this.doc.createRange();
17184         }
17185     },
17186     getParentElement: function()
17187     {
17188         
17189         this.assignDocWin();
17190         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17191         
17192         var range = this.createRange(sel);
17193          
17194         try {
17195             var p = range.commonAncestorContainer;
17196             while (p.nodeType == 3) { // text node
17197                 p = p.parentNode;
17198             }
17199             return p;
17200         } catch (e) {
17201             return null;
17202         }
17203     
17204     },
17205     /***
17206      *
17207      * Range intersection.. the hard stuff...
17208      *  '-1' = before
17209      *  '0' = hits..
17210      *  '1' = after.
17211      *         [ -- selected range --- ]
17212      *   [fail]                        [fail]
17213      *
17214      *    basically..
17215      *      if end is before start or  hits it. fail.
17216      *      if start is after end or hits it fail.
17217      *
17218      *   if either hits (but other is outside. - then it's not 
17219      *   
17220      *    
17221      **/
17222     
17223     
17224     // @see http://www.thismuchiknow.co.uk/?p=64.
17225     rangeIntersectsNode : function(range, node)
17226     {
17227         var nodeRange = node.ownerDocument.createRange();
17228         try {
17229             nodeRange.selectNode(node);
17230         } catch (e) {
17231             nodeRange.selectNodeContents(node);
17232         }
17233     
17234         var rangeStartRange = range.cloneRange();
17235         rangeStartRange.collapse(true);
17236     
17237         var rangeEndRange = range.cloneRange();
17238         rangeEndRange.collapse(false);
17239     
17240         var nodeStartRange = nodeRange.cloneRange();
17241         nodeStartRange.collapse(true);
17242     
17243         var nodeEndRange = nodeRange.cloneRange();
17244         nodeEndRange.collapse(false);
17245     
17246         return rangeStartRange.compareBoundaryPoints(
17247                  Range.START_TO_START, nodeEndRange) == -1 &&
17248                rangeEndRange.compareBoundaryPoints(
17249                  Range.START_TO_START, nodeStartRange) == 1;
17250         
17251          
17252     },
17253     rangeCompareNode : function(range, node)
17254     {
17255         var nodeRange = node.ownerDocument.createRange();
17256         try {
17257             nodeRange.selectNode(node);
17258         } catch (e) {
17259             nodeRange.selectNodeContents(node);
17260         }
17261         
17262         
17263         range.collapse(true);
17264     
17265         nodeRange.collapse(true);
17266      
17267         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17268         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17269          
17270         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17271         
17272         var nodeIsBefore   =  ss == 1;
17273         var nodeIsAfter    = ee == -1;
17274         
17275         if (nodeIsBefore && nodeIsAfter)
17276             return 0; // outer
17277         if (!nodeIsBefore && nodeIsAfter)
17278             return 1; //right trailed.
17279         
17280         if (nodeIsBefore && !nodeIsAfter)
17281             return 2;  // left trailed.
17282         // fully contined.
17283         return 3;
17284     },
17285
17286     // private? - in a new class?
17287     cleanUpPaste :  function()
17288     {
17289         // cleans up the whole document..
17290         Roo.log('cleanuppaste');
17291         
17292         this.cleanUpChildren(this.doc.body);
17293         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17294         if (clean != this.doc.body.innerHTML) {
17295             this.doc.body.innerHTML = clean;
17296         }
17297         
17298     },
17299     
17300     cleanWordChars : function(input) {// change the chars to hex code
17301         var he = Roo.HtmlEditorCore;
17302         
17303         var output = input;
17304         Roo.each(he.swapCodes, function(sw) { 
17305             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17306             
17307             output = output.replace(swapper, sw[1]);
17308         });
17309         
17310         return output;
17311     },
17312     
17313     
17314     cleanUpChildren : function (n)
17315     {
17316         if (!n.childNodes.length) {
17317             return;
17318         }
17319         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17320            this.cleanUpChild(n.childNodes[i]);
17321         }
17322     },
17323     
17324     
17325         
17326     
17327     cleanUpChild : function (node)
17328     {
17329         var ed = this;
17330         //console.log(node);
17331         if (node.nodeName == "#text") {
17332             // clean up silly Windows -- stuff?
17333             return; 
17334         }
17335         if (node.nodeName == "#comment") {
17336             node.parentNode.removeChild(node);
17337             // clean up silly Windows -- stuff?
17338             return; 
17339         }
17340         var lcname = node.tagName.toLowerCase();
17341         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17342         // whitelist of tags..
17343         
17344         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17345             // remove node.
17346             node.parentNode.removeChild(node);
17347             return;
17348             
17349         }
17350         
17351         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17352         
17353         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17354         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17355         
17356         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17357         //    remove_keep_children = true;
17358         //}
17359         
17360         if (remove_keep_children) {
17361             this.cleanUpChildren(node);
17362             // inserts everything just before this node...
17363             while (node.childNodes.length) {
17364                 var cn = node.childNodes[0];
17365                 node.removeChild(cn);
17366                 node.parentNode.insertBefore(cn, node);
17367             }
17368             node.parentNode.removeChild(node);
17369             return;
17370         }
17371         
17372         if (!node.attributes || !node.attributes.length) {
17373             this.cleanUpChildren(node);
17374             return;
17375         }
17376         
17377         function cleanAttr(n,v)
17378         {
17379             
17380             if (v.match(/^\./) || v.match(/^\//)) {
17381                 return;
17382             }
17383             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17384                 return;
17385             }
17386             if (v.match(/^#/)) {
17387                 return;
17388             }
17389 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17390             node.removeAttribute(n);
17391             
17392         }
17393         
17394         var cwhite = this.cwhite;
17395         var cblack = this.cblack;
17396             
17397         function cleanStyle(n,v)
17398         {
17399             if (v.match(/expression/)) { //XSS?? should we even bother..
17400                 node.removeAttribute(n);
17401                 return;
17402             }
17403             
17404             var parts = v.split(/;/);
17405             var clean = [];
17406             
17407             Roo.each(parts, function(p) {
17408                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17409                 if (!p.length) {
17410                     return true;
17411                 }
17412                 var l = p.split(':').shift().replace(/\s+/g,'');
17413                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17414                 
17415                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17416 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17417                     //node.removeAttribute(n);
17418                     return true;
17419                 }
17420                 //Roo.log()
17421                 // only allow 'c whitelisted system attributes'
17422                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17423 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17424                     //node.removeAttribute(n);
17425                     return true;
17426                 }
17427                 
17428                 
17429                  
17430                 
17431                 clean.push(p);
17432                 return true;
17433             });
17434             if (clean.length) { 
17435                 node.setAttribute(n, clean.join(';'));
17436             } else {
17437                 node.removeAttribute(n);
17438             }
17439             
17440         }
17441         
17442         
17443         for (var i = node.attributes.length-1; i > -1 ; i--) {
17444             var a = node.attributes[i];
17445             //console.log(a);
17446             
17447             if (a.name.toLowerCase().substr(0,2)=='on')  {
17448                 node.removeAttribute(a.name);
17449                 continue;
17450             }
17451             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17452                 node.removeAttribute(a.name);
17453                 continue;
17454             }
17455             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17456                 cleanAttr(a.name,a.value); // fixme..
17457                 continue;
17458             }
17459             if (a.name == 'style') {
17460                 cleanStyle(a.name,a.value);
17461                 continue;
17462             }
17463             /// clean up MS crap..
17464             // tecnically this should be a list of valid class'es..
17465             
17466             
17467             if (a.name == 'class') {
17468                 if (a.value.match(/^Mso/)) {
17469                     node.className = '';
17470                 }
17471                 
17472                 if (a.value.match(/body/)) {
17473                     node.className = '';
17474                 }
17475                 continue;
17476             }
17477             
17478             // style cleanup!?
17479             // class cleanup?
17480             
17481         }
17482         
17483         
17484         this.cleanUpChildren(node);
17485         
17486         
17487     },
17488     /**
17489      * Clean up MS wordisms...
17490      */
17491     cleanWord : function(node)
17492     {
17493         var _t = this;
17494         var cleanWordChildren = function()
17495         {
17496             if (!node.childNodes.length) {
17497                 return;
17498             }
17499             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17500                _t.cleanWord(node.childNodes[i]);
17501             }
17502         }
17503         
17504         
17505         if (!node) {
17506             this.cleanWord(this.doc.body);
17507             return;
17508         }
17509         if (node.nodeName == "#text") {
17510             // clean up silly Windows -- stuff?
17511             return; 
17512         }
17513         if (node.nodeName == "#comment") {
17514             node.parentNode.removeChild(node);
17515             // clean up silly Windows -- stuff?
17516             return; 
17517         }
17518         
17519         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17520             node.parentNode.removeChild(node);
17521             return;
17522         }
17523         
17524         // remove - but keep children..
17525         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17526             while (node.childNodes.length) {
17527                 var cn = node.childNodes[0];
17528                 node.removeChild(cn);
17529                 node.parentNode.insertBefore(cn, node);
17530             }
17531             node.parentNode.removeChild(node);
17532             cleanWordChildren();
17533             return;
17534         }
17535         // clean styles
17536         if (node.className.length) {
17537             
17538             var cn = node.className.split(/\W+/);
17539             var cna = [];
17540             Roo.each(cn, function(cls) {
17541                 if (cls.match(/Mso[a-zA-Z]+/)) {
17542                     return;
17543                 }
17544                 cna.push(cls);
17545             });
17546             node.className = cna.length ? cna.join(' ') : '';
17547             if (!cna.length) {
17548                 node.removeAttribute("class");
17549             }
17550         }
17551         
17552         if (node.hasAttribute("lang")) {
17553             node.removeAttribute("lang");
17554         }
17555         
17556         if (node.hasAttribute("style")) {
17557             
17558             var styles = node.getAttribute("style").split(";");
17559             var nstyle = [];
17560             Roo.each(styles, function(s) {
17561                 if (!s.match(/:/)) {
17562                     return;
17563                 }
17564                 var kv = s.split(":");
17565                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17566                     return;
17567                 }
17568                 // what ever is left... we allow.
17569                 nstyle.push(s);
17570             });
17571             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17572             if (!nstyle.length) {
17573                 node.removeAttribute('style');
17574             }
17575         }
17576         
17577         cleanWordChildren();
17578         
17579         
17580     },
17581     domToHTML : function(currentElement, depth, nopadtext) {
17582         
17583         depth = depth || 0;
17584         nopadtext = nopadtext || false;
17585     
17586         if (!currentElement) {
17587             return this.domToHTML(this.doc.body);
17588         }
17589         
17590         //Roo.log(currentElement);
17591         var j;
17592         var allText = false;
17593         var nodeName = currentElement.nodeName;
17594         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17595         
17596         if  (nodeName == '#text') {
17597             return currentElement.nodeValue;
17598         }
17599         
17600         
17601         var ret = '';
17602         if (nodeName != 'BODY') {
17603              
17604             var i = 0;
17605             // Prints the node tagName, such as <A>, <IMG>, etc
17606             if (tagName) {
17607                 var attr = [];
17608                 for(i = 0; i < currentElement.attributes.length;i++) {
17609                     // quoting?
17610                     var aname = currentElement.attributes.item(i).name;
17611                     if (!currentElement.attributes.item(i).value.length) {
17612                         continue;
17613                     }
17614                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17615                 }
17616                 
17617                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17618             } 
17619             else {
17620                 
17621                 // eack
17622             }
17623         } else {
17624             tagName = false;
17625         }
17626         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17627             return ret;
17628         }
17629         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17630             nopadtext = true;
17631         }
17632         
17633         
17634         // Traverse the tree
17635         i = 0;
17636         var currentElementChild = currentElement.childNodes.item(i);
17637         var allText = true;
17638         var innerHTML  = '';
17639         lastnode = '';
17640         while (currentElementChild) {
17641             // Formatting code (indent the tree so it looks nice on the screen)
17642             var nopad = nopadtext;
17643             if (lastnode == 'SPAN') {
17644                 nopad  = true;
17645             }
17646             // text
17647             if  (currentElementChild.nodeName == '#text') {
17648                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17649                 if (!nopad && toadd.length > 80) {
17650                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17651                 }
17652                 innerHTML  += toadd;
17653                 
17654                 i++;
17655                 currentElementChild = currentElement.childNodes.item(i);
17656                 lastNode = '';
17657                 continue;
17658             }
17659             allText = false;
17660             
17661             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17662                 
17663             // Recursively traverse the tree structure of the child node
17664             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17665             lastnode = currentElementChild.nodeName;
17666             i++;
17667             currentElementChild=currentElement.childNodes.item(i);
17668         }
17669         
17670         ret += innerHTML;
17671         
17672         if (!allText) {
17673                 // The remaining code is mostly for formatting the tree
17674             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17675         }
17676         
17677         
17678         if (tagName) {
17679             ret+= "</"+tagName+">";
17680         }
17681         return ret;
17682         
17683     },
17684         
17685     applyBlacklists : function()
17686     {
17687         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17688         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17689         
17690         this.white = [];
17691         this.black = [];
17692         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17693             if (b.indexOf(tag) > -1) {
17694                 return;
17695             }
17696             this.white.push(tag);
17697             
17698         }, this);
17699         
17700         Roo.each(w, function(tag) {
17701             if (b.indexOf(tag) > -1) {
17702                 return;
17703             }
17704             if (this.white.indexOf(tag) > -1) {
17705                 return;
17706             }
17707             this.white.push(tag);
17708             
17709         }, this);
17710         
17711         
17712         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17713             if (w.indexOf(tag) > -1) {
17714                 return;
17715             }
17716             this.black.push(tag);
17717             
17718         }, this);
17719         
17720         Roo.each(b, function(tag) {
17721             if (w.indexOf(tag) > -1) {
17722                 return;
17723             }
17724             if (this.black.indexOf(tag) > -1) {
17725                 return;
17726             }
17727             this.black.push(tag);
17728             
17729         }, this);
17730         
17731         
17732         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17733         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17734         
17735         this.cwhite = [];
17736         this.cblack = [];
17737         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17738             if (b.indexOf(tag) > -1) {
17739                 return;
17740             }
17741             this.cwhite.push(tag);
17742             
17743         }, this);
17744         
17745         Roo.each(w, function(tag) {
17746             if (b.indexOf(tag) > -1) {
17747                 return;
17748             }
17749             if (this.cwhite.indexOf(tag) > -1) {
17750                 return;
17751             }
17752             this.cwhite.push(tag);
17753             
17754         }, this);
17755         
17756         
17757         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17758             if (w.indexOf(tag) > -1) {
17759                 return;
17760             }
17761             this.cblack.push(tag);
17762             
17763         }, this);
17764         
17765         Roo.each(b, function(tag) {
17766             if (w.indexOf(tag) > -1) {
17767                 return;
17768             }
17769             if (this.cblack.indexOf(tag) > -1) {
17770                 return;
17771             }
17772             this.cblack.push(tag);
17773             
17774         }, this);
17775     }
17776     
17777     // hide stuff that is not compatible
17778     /**
17779      * @event blur
17780      * @hide
17781      */
17782     /**
17783      * @event change
17784      * @hide
17785      */
17786     /**
17787      * @event focus
17788      * @hide
17789      */
17790     /**
17791      * @event specialkey
17792      * @hide
17793      */
17794     /**
17795      * @cfg {String} fieldClass @hide
17796      */
17797     /**
17798      * @cfg {String} focusClass @hide
17799      */
17800     /**
17801      * @cfg {String} autoCreate @hide
17802      */
17803     /**
17804      * @cfg {String} inputType @hide
17805      */
17806     /**
17807      * @cfg {String} invalidClass @hide
17808      */
17809     /**
17810      * @cfg {String} invalidText @hide
17811      */
17812     /**
17813      * @cfg {String} msgFx @hide
17814      */
17815     /**
17816      * @cfg {String} validateOnBlur @hide
17817      */
17818 });
17819
17820 Roo.HtmlEditorCore.white = [
17821         'area', 'br', 'img', 'input', 'hr', 'wbr',
17822         
17823        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17824        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17825        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17826        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17827        'table',   'ul',         'xmp', 
17828        
17829        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17830       'thead',   'tr', 
17831      
17832       'dir', 'menu', 'ol', 'ul', 'dl',
17833        
17834       'embed',  'object'
17835 ];
17836
17837
17838 Roo.HtmlEditorCore.black = [
17839     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17840         'applet', // 
17841         'base',   'basefont', 'bgsound', 'blink',  'body', 
17842         'frame',  'frameset', 'head',    'html',   'ilayer', 
17843         'iframe', 'layer',  'link',     'meta',    'object',   
17844         'script', 'style' ,'title',  'xml' // clean later..
17845 ];
17846 Roo.HtmlEditorCore.clean = [
17847     'script', 'style', 'title', 'xml'
17848 ];
17849 Roo.HtmlEditorCore.remove = [
17850     'font'
17851 ];
17852 // attributes..
17853
17854 Roo.HtmlEditorCore.ablack = [
17855     'on'
17856 ];
17857     
17858 Roo.HtmlEditorCore.aclean = [ 
17859     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17860 ];
17861
17862 // protocols..
17863 Roo.HtmlEditorCore.pwhite= [
17864         'http',  'https',  'mailto'
17865 ];
17866
17867 // white listed style attributes.
17868 Roo.HtmlEditorCore.cwhite= [
17869       //  'text-align', /// default is to allow most things..
17870       
17871          
17872 //        'font-size'//??
17873 ];
17874
17875 // black listed style attributes.
17876 Roo.HtmlEditorCore.cblack= [
17877       //  'font-size' -- this can be set by the project 
17878 ];
17879
17880
17881 Roo.HtmlEditorCore.swapCodes   =[ 
17882     [    8211, "--" ], 
17883     [    8212, "--" ], 
17884     [    8216,  "'" ],  
17885     [    8217, "'" ],  
17886     [    8220, '"' ],  
17887     [    8221, '"' ],  
17888     [    8226, "*" ],  
17889     [    8230, "..." ]
17890 ]; 
17891
17892     /*
17893  * - LGPL
17894  *
17895  * HtmlEditor
17896  * 
17897  */
17898
17899 /**
17900  * @class Roo.bootstrap.HtmlEditor
17901  * @extends Roo.bootstrap.TextArea
17902  * Bootstrap HtmlEditor class
17903
17904  * @constructor
17905  * Create a new HtmlEditor
17906  * @param {Object} config The config object
17907  */
17908
17909 Roo.bootstrap.HtmlEditor = function(config){
17910     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17911     if (!this.toolbars) {
17912         this.toolbars = [];
17913     }
17914     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17915     this.addEvents({
17916             /**
17917              * @event initialize
17918              * Fires when the editor is fully initialized (including the iframe)
17919              * @param {HtmlEditor} this
17920              */
17921             initialize: true,
17922             /**
17923              * @event activate
17924              * Fires when the editor is first receives the focus. Any insertion must wait
17925              * until after this event.
17926              * @param {HtmlEditor} this
17927              */
17928             activate: true,
17929              /**
17930              * @event beforesync
17931              * Fires before the textarea is updated with content from the editor iframe. Return false
17932              * to cancel the sync.
17933              * @param {HtmlEditor} this
17934              * @param {String} html
17935              */
17936             beforesync: true,
17937              /**
17938              * @event beforepush
17939              * Fires before the iframe editor is updated with content from the textarea. Return false
17940              * to cancel the push.
17941              * @param {HtmlEditor} this
17942              * @param {String} html
17943              */
17944             beforepush: true,
17945              /**
17946              * @event sync
17947              * Fires when the textarea is updated with content from the editor iframe.
17948              * @param {HtmlEditor} this
17949              * @param {String} html
17950              */
17951             sync: true,
17952              /**
17953              * @event push
17954              * Fires when the iframe editor is updated with content from the textarea.
17955              * @param {HtmlEditor} this
17956              * @param {String} html
17957              */
17958             push: true,
17959              /**
17960              * @event editmodechange
17961              * Fires when the editor switches edit modes
17962              * @param {HtmlEditor} this
17963              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17964              */
17965             editmodechange: true,
17966             /**
17967              * @event editorevent
17968              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17969              * @param {HtmlEditor} this
17970              */
17971             editorevent: true,
17972             /**
17973              * @event firstfocus
17974              * Fires when on first focus - needed by toolbars..
17975              * @param {HtmlEditor} this
17976              */
17977             firstfocus: true,
17978             /**
17979              * @event autosave
17980              * Auto save the htmlEditor value as a file into Events
17981              * @param {HtmlEditor} this
17982              */
17983             autosave: true,
17984             /**
17985              * @event savedpreview
17986              * preview the saved version of htmlEditor
17987              * @param {HtmlEditor} this
17988              */
17989             savedpreview: true
17990         });
17991 };
17992
17993
17994 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17995     
17996     
17997       /**
17998      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17999      */
18000     toolbars : false,
18001    
18002      /**
18003      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18004      *                        Roo.resizable.
18005      */
18006     resizable : false,
18007      /**
18008      * @cfg {Number} height (in pixels)
18009      */   
18010     height: 300,
18011    /**
18012      * @cfg {Number} width (in pixels)
18013      */   
18014     width: false,
18015     
18016     /**
18017      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18018      * 
18019      */
18020     stylesheets: false,
18021     
18022     // id of frame..
18023     frameId: false,
18024     
18025     // private properties
18026     validationEvent : false,
18027     deferHeight: true,
18028     initialized : false,
18029     activated : false,
18030     
18031     onFocus : Roo.emptyFn,
18032     iframePad:3,
18033     hideMode:'offsets',
18034     
18035     
18036     tbContainer : false,
18037     
18038     toolbarContainer :function() {
18039         return this.wrap.select('.x-html-editor-tb',true).first();
18040     },
18041
18042     /**
18043      * Protected method that will not generally be called directly. It
18044      * is called when the editor creates its toolbar. Override this method if you need to
18045      * add custom toolbar buttons.
18046      * @param {HtmlEditor} editor
18047      */
18048     createToolbar : function(){
18049         
18050         Roo.log("create toolbars");
18051         
18052         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18053         this.toolbars[0].render(this.toolbarContainer());
18054         
18055         return;
18056         
18057 //        if (!editor.toolbars || !editor.toolbars.length) {
18058 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18059 //        }
18060 //        
18061 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18062 //            editor.toolbars[i] = Roo.factory(
18063 //                    typeof(editor.toolbars[i]) == 'string' ?
18064 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18065 //                Roo.bootstrap.HtmlEditor);
18066 //            editor.toolbars[i].init(editor);
18067 //        }
18068     },
18069
18070      
18071     // private
18072     onRender : function(ct, position)
18073     {
18074        // Roo.log("Call onRender: " + this.xtype);
18075         var _t = this;
18076         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18077       
18078         this.wrap = this.inputEl().wrap({
18079             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18080         });
18081         
18082         this.editorcore.onRender(ct, position);
18083          
18084         if (this.resizable) {
18085             this.resizeEl = new Roo.Resizable(this.wrap, {
18086                 pinned : true,
18087                 wrap: true,
18088                 dynamic : true,
18089                 minHeight : this.height,
18090                 height: this.height,
18091                 handles : this.resizable,
18092                 width: this.width,
18093                 listeners : {
18094                     resize : function(r, w, h) {
18095                         _t.onResize(w,h); // -something
18096                     }
18097                 }
18098             });
18099             
18100         }
18101         this.createToolbar(this);
18102        
18103         
18104         if(!this.width && this.resizable){
18105             this.setSize(this.wrap.getSize());
18106         }
18107         if (this.resizeEl) {
18108             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18109             // should trigger onReize..
18110         }
18111         
18112     },
18113
18114     // private
18115     onResize : function(w, h)
18116     {
18117         Roo.log('resize: ' +w + ',' + h );
18118         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18119         var ew = false;
18120         var eh = false;
18121         
18122         if(this.inputEl() ){
18123             if(typeof w == 'number'){
18124                 var aw = w - this.wrap.getFrameWidth('lr');
18125                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18126                 ew = aw;
18127             }
18128             if(typeof h == 'number'){
18129                  var tbh = -11;  // fixme it needs to tool bar size!
18130                 for (var i =0; i < this.toolbars.length;i++) {
18131                     // fixme - ask toolbars for heights?
18132                     tbh += this.toolbars[i].el.getHeight();
18133                     //if (this.toolbars[i].footer) {
18134                     //    tbh += this.toolbars[i].footer.el.getHeight();
18135                     //}
18136                 }
18137               
18138                 
18139                 
18140                 
18141                 
18142                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18143                 ah -= 5; // knock a few pixes off for look..
18144                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18145                 var eh = ah;
18146             }
18147         }
18148         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18149         this.editorcore.onResize(ew,eh);
18150         
18151     },
18152
18153     /**
18154      * Toggles the editor between standard and source edit mode.
18155      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18156      */
18157     toggleSourceEdit : function(sourceEditMode)
18158     {
18159         this.editorcore.toggleSourceEdit(sourceEditMode);
18160         
18161         if(this.editorcore.sourceEditMode){
18162             Roo.log('editor - showing textarea');
18163             
18164 //            Roo.log('in');
18165 //            Roo.log(this.syncValue());
18166             this.syncValue();
18167             this.inputEl().removeClass(['hide', 'x-hidden']);
18168             this.inputEl().dom.removeAttribute('tabIndex');
18169             this.inputEl().focus();
18170         }else{
18171             Roo.log('editor - hiding textarea');
18172 //            Roo.log('out')
18173 //            Roo.log(this.pushValue()); 
18174             this.pushValue();
18175             
18176             this.inputEl().addClass(['hide', 'x-hidden']);
18177             this.inputEl().dom.setAttribute('tabIndex', -1);
18178             //this.deferFocus();
18179         }
18180          
18181         if(this.resizable){
18182             this.setSize(this.wrap.getSize());
18183         }
18184         
18185         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18186     },
18187  
18188     // private (for BoxComponent)
18189     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18190
18191     // private (for BoxComponent)
18192     getResizeEl : function(){
18193         return this.wrap;
18194     },
18195
18196     // private (for BoxComponent)
18197     getPositionEl : function(){
18198         return this.wrap;
18199     },
18200
18201     // private
18202     initEvents : function(){
18203         this.originalValue = this.getValue();
18204     },
18205
18206 //    /**
18207 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18208 //     * @method
18209 //     */
18210 //    markInvalid : Roo.emptyFn,
18211 //    /**
18212 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18213 //     * @method
18214 //     */
18215 //    clearInvalid : Roo.emptyFn,
18216
18217     setValue : function(v){
18218         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18219         this.editorcore.pushValue();
18220     },
18221
18222      
18223     // private
18224     deferFocus : function(){
18225         this.focus.defer(10, this);
18226     },
18227
18228     // doc'ed in Field
18229     focus : function(){
18230         this.editorcore.focus();
18231         
18232     },
18233       
18234
18235     // private
18236     onDestroy : function(){
18237         
18238         
18239         
18240         if(this.rendered){
18241             
18242             for (var i =0; i < this.toolbars.length;i++) {
18243                 // fixme - ask toolbars for heights?
18244                 this.toolbars[i].onDestroy();
18245             }
18246             
18247             this.wrap.dom.innerHTML = '';
18248             this.wrap.remove();
18249         }
18250     },
18251
18252     // private
18253     onFirstFocus : function(){
18254         //Roo.log("onFirstFocus");
18255         this.editorcore.onFirstFocus();
18256          for (var i =0; i < this.toolbars.length;i++) {
18257             this.toolbars[i].onFirstFocus();
18258         }
18259         
18260     },
18261     
18262     // private
18263     syncValue : function()
18264     {   
18265         this.editorcore.syncValue();
18266     },
18267     
18268     pushValue : function()
18269     {   
18270         this.editorcore.pushValue();
18271     }
18272      
18273     
18274     // hide stuff that is not compatible
18275     /**
18276      * @event blur
18277      * @hide
18278      */
18279     /**
18280      * @event change
18281      * @hide
18282      */
18283     /**
18284      * @event focus
18285      * @hide
18286      */
18287     /**
18288      * @event specialkey
18289      * @hide
18290      */
18291     /**
18292      * @cfg {String} fieldClass @hide
18293      */
18294     /**
18295      * @cfg {String} focusClass @hide
18296      */
18297     /**
18298      * @cfg {String} autoCreate @hide
18299      */
18300     /**
18301      * @cfg {String} inputType @hide
18302      */
18303     /**
18304      * @cfg {String} invalidClass @hide
18305      */
18306     /**
18307      * @cfg {String} invalidText @hide
18308      */
18309     /**
18310      * @cfg {String} msgFx @hide
18311      */
18312     /**
18313      * @cfg {String} validateOnBlur @hide
18314      */
18315 });
18316  
18317     
18318    
18319    
18320    
18321       
18322 Roo.namespace('Roo.bootstrap.htmleditor');
18323 /**
18324  * @class Roo.bootstrap.HtmlEditorToolbar1
18325  * Basic Toolbar
18326  * 
18327  * Usage:
18328  *
18329  new Roo.bootstrap.HtmlEditor({
18330     ....
18331     toolbars : [
18332         new Roo.bootstrap.HtmlEditorToolbar1({
18333             disable : { fonts: 1 , format: 1, ..., ... , ...],
18334             btns : [ .... ]
18335         })
18336     }
18337      
18338  * 
18339  * @cfg {Object} disable List of elements to disable..
18340  * @cfg {Array} btns List of additional buttons.
18341  * 
18342  * 
18343  * NEEDS Extra CSS? 
18344  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18345  */
18346  
18347 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18348 {
18349     
18350     Roo.apply(this, config);
18351     
18352     // default disabled, based on 'good practice'..
18353     this.disable = this.disable || {};
18354     Roo.applyIf(this.disable, {
18355         fontSize : true,
18356         colors : true,
18357         specialElements : true
18358     });
18359     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18360     
18361     this.editor = config.editor;
18362     this.editorcore = config.editor.editorcore;
18363     
18364     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18365     
18366     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18367     // dont call parent... till later.
18368 }
18369 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18370      
18371     bar : true,
18372     
18373     editor : false,
18374     editorcore : false,
18375     
18376     
18377     formats : [
18378         "p" ,  
18379         "h1","h2","h3","h4","h5","h6", 
18380         "pre", "code", 
18381         "abbr", "acronym", "address", "cite", "samp", "var",
18382         'div','span'
18383     ],
18384     
18385     onRender : function(ct, position)
18386     {
18387        // Roo.log("Call onRender: " + this.xtype);
18388         
18389        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18390        Roo.log(this.el);
18391        this.el.dom.style.marginBottom = '0';
18392        var _this = this;
18393        var editorcore = this.editorcore;
18394        var editor= this.editor;
18395        
18396        var children = [];
18397        var btn = function(id,cmd , toggle, handler){
18398        
18399             var  event = toggle ? 'toggle' : 'click';
18400        
18401             var a = {
18402                 size : 'sm',
18403                 xtype: 'Button',
18404                 xns: Roo.bootstrap,
18405                 glyphicon : id,
18406                 cmd : id || cmd,
18407                 enableToggle:toggle !== false,
18408                 //html : 'submit'
18409                 pressed : toggle ? false : null,
18410                 listeners : {}
18411             }
18412             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18413                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18414             }
18415             children.push(a);
18416             return a;
18417        }
18418         
18419         var style = {
18420                 xtype: 'Button',
18421                 size : 'sm',
18422                 xns: Roo.bootstrap,
18423                 glyphicon : 'font',
18424                 //html : 'submit'
18425                 menu : {
18426                     xtype: 'Menu',
18427                     xns: Roo.bootstrap,
18428                     items:  []
18429                 }
18430         };
18431         Roo.each(this.formats, function(f) {
18432             style.menu.items.push({
18433                 xtype :'MenuItem',
18434                 xns: Roo.bootstrap,
18435                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18436                 tagname : f,
18437                 listeners : {
18438                     click : function()
18439                     {
18440                         editorcore.insertTag(this.tagname);
18441                         editor.focus();
18442                     }
18443                 }
18444                 
18445             });
18446         });
18447          children.push(style);   
18448             
18449             
18450         btn('bold',false,true);
18451         btn('italic',false,true);
18452         btn('align-left', 'justifyleft',true);
18453         btn('align-center', 'justifycenter',true);
18454         btn('align-right' , 'justifyright',true);
18455         btn('link', false, false, function(btn) {
18456             //Roo.log("create link?");
18457             var url = prompt(this.createLinkText, this.defaultLinkValue);
18458             if(url && url != 'http:/'+'/'){
18459                 this.editorcore.relayCmd('createlink', url);
18460             }
18461         }),
18462         btn('list','insertunorderedlist',true);
18463         btn('pencil', false,true, function(btn){
18464                 Roo.log(this);
18465                 
18466                 this.toggleSourceEdit(btn.pressed);
18467         });
18468         /*
18469         var cog = {
18470                 xtype: 'Button',
18471                 size : 'sm',
18472                 xns: Roo.bootstrap,
18473                 glyphicon : 'cog',
18474                 //html : 'submit'
18475                 menu : {
18476                     xtype: 'Menu',
18477                     xns: Roo.bootstrap,
18478                     items:  []
18479                 }
18480         };
18481         
18482         cog.menu.items.push({
18483             xtype :'MenuItem',
18484             xns: Roo.bootstrap,
18485             html : Clean styles,
18486             tagname : f,
18487             listeners : {
18488                 click : function()
18489                 {
18490                     editorcore.insertTag(this.tagname);
18491                     editor.focus();
18492                 }
18493             }
18494             
18495         });
18496        */
18497         
18498          
18499        this.xtype = 'NavSimplebar';
18500         
18501         for(var i=0;i< children.length;i++) {
18502             
18503             this.buttons.add(this.addxtypeChild(children[i]));
18504             
18505         }
18506         
18507         editor.on('editorevent', this.updateToolbar, this);
18508     },
18509     onBtnClick : function(id)
18510     {
18511        this.editorcore.relayCmd(id);
18512        this.editorcore.focus();
18513     },
18514     
18515     /**
18516      * Protected method that will not generally be called directly. It triggers
18517      * a toolbar update by reading the markup state of the current selection in the editor.
18518      */
18519     updateToolbar: function(){
18520
18521         if(!this.editorcore.activated){
18522             this.editor.onFirstFocus(); // is this neeed?
18523             return;
18524         }
18525
18526         var btns = this.buttons; 
18527         var doc = this.editorcore.doc;
18528         btns.get('bold').setActive(doc.queryCommandState('bold'));
18529         btns.get('italic').setActive(doc.queryCommandState('italic'));
18530         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18531         
18532         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18533         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18534         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18535         
18536         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18537         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18538          /*
18539         
18540         var ans = this.editorcore.getAllAncestors();
18541         if (this.formatCombo) {
18542             
18543             
18544             var store = this.formatCombo.store;
18545             this.formatCombo.setValue("");
18546             for (var i =0; i < ans.length;i++) {
18547                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18548                     // select it..
18549                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18550                     break;
18551                 }
18552             }
18553         }
18554         
18555         
18556         
18557         // hides menus... - so this cant be on a menu...
18558         Roo.bootstrap.MenuMgr.hideAll();
18559         */
18560         Roo.bootstrap.MenuMgr.hideAll();
18561         //this.editorsyncValue();
18562     },
18563     onFirstFocus: function() {
18564         this.buttons.each(function(item){
18565            item.enable();
18566         });
18567     },
18568     toggleSourceEdit : function(sourceEditMode){
18569         
18570           
18571         if(sourceEditMode){
18572             Roo.log("disabling buttons");
18573            this.buttons.each( function(item){
18574                 if(item.cmd != 'pencil'){
18575                     item.disable();
18576                 }
18577             });
18578           
18579         }else{
18580             Roo.log("enabling buttons");
18581             if(this.editorcore.initialized){
18582                 this.buttons.each( function(item){
18583                     item.enable();
18584                 });
18585             }
18586             
18587         }
18588         Roo.log("calling toggole on editor");
18589         // tell the editor that it's been pressed..
18590         this.editor.toggleSourceEdit(sourceEditMode);
18591        
18592     }
18593 });
18594
18595
18596
18597
18598
18599 /**
18600  * @class Roo.bootstrap.Table.AbstractSelectionModel
18601  * @extends Roo.util.Observable
18602  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18603  * implemented by descendant classes.  This class should not be directly instantiated.
18604  * @constructor
18605  */
18606 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18607     this.locked = false;
18608     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18609 };
18610
18611
18612 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18613     /** @ignore Called by the grid automatically. Do not call directly. */
18614     init : function(grid){
18615         this.grid = grid;
18616         this.initEvents();
18617     },
18618
18619     /**
18620      * Locks the selections.
18621      */
18622     lock : function(){
18623         this.locked = true;
18624     },
18625
18626     /**
18627      * Unlocks the selections.
18628      */
18629     unlock : function(){
18630         this.locked = false;
18631     },
18632
18633     /**
18634      * Returns true if the selections are locked.
18635      * @return {Boolean}
18636      */
18637     isLocked : function(){
18638         return this.locked;
18639     }
18640 });
18641 /**
18642  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18643  * @class Roo.bootstrap.Table.RowSelectionModel
18644  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18645  * It supports multiple selections and keyboard selection/navigation. 
18646  * @constructor
18647  * @param {Object} config
18648  */
18649
18650 Roo.bootstrap.Table.RowSelectionModel = function(config){
18651     Roo.apply(this, config);
18652     this.selections = new Roo.util.MixedCollection(false, function(o){
18653         return o.id;
18654     });
18655
18656     this.last = false;
18657     this.lastActive = false;
18658
18659     this.addEvents({
18660         /**
18661              * @event selectionchange
18662              * Fires when the selection changes
18663              * @param {SelectionModel} this
18664              */
18665             "selectionchange" : true,
18666         /**
18667              * @event afterselectionchange
18668              * Fires after the selection changes (eg. by key press or clicking)
18669              * @param {SelectionModel} this
18670              */
18671             "afterselectionchange" : true,
18672         /**
18673              * @event beforerowselect
18674              * Fires when a row is selected being selected, return false to cancel.
18675              * @param {SelectionModel} this
18676              * @param {Number} rowIndex The selected index
18677              * @param {Boolean} keepExisting False if other selections will be cleared
18678              */
18679             "beforerowselect" : true,
18680         /**
18681              * @event rowselect
18682              * Fires when a row is selected.
18683              * @param {SelectionModel} this
18684              * @param {Number} rowIndex The selected index
18685              * @param {Roo.data.Record} r The record
18686              */
18687             "rowselect" : true,
18688         /**
18689              * @event rowdeselect
18690              * Fires when a row is deselected.
18691              * @param {SelectionModel} this
18692              * @param {Number} rowIndex The selected index
18693              */
18694         "rowdeselect" : true
18695     });
18696     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18697     this.locked = false;
18698 };
18699
18700 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18701     /**
18702      * @cfg {Boolean} singleSelect
18703      * True to allow selection of only one row at a time (defaults to false)
18704      */
18705     singleSelect : false,
18706
18707     // private
18708     initEvents : function(){
18709
18710         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18711             this.grid.on("mousedown", this.handleMouseDown, this);
18712         }else{ // allow click to work like normal
18713             this.grid.on("rowclick", this.handleDragableRowClick, this);
18714         }
18715
18716         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18717             "up" : function(e){
18718                 if(!e.shiftKey){
18719                     this.selectPrevious(e.shiftKey);
18720                 }else if(this.last !== false && this.lastActive !== false){
18721                     var last = this.last;
18722                     this.selectRange(this.last,  this.lastActive-1);
18723                     this.grid.getView().focusRow(this.lastActive);
18724                     if(last !== false){
18725                         this.last = last;
18726                     }
18727                 }else{
18728                     this.selectFirstRow();
18729                 }
18730                 this.fireEvent("afterselectionchange", this);
18731             },
18732             "down" : function(e){
18733                 if(!e.shiftKey){
18734                     this.selectNext(e.shiftKey);
18735                 }else if(this.last !== false && this.lastActive !== false){
18736                     var last = this.last;
18737                     this.selectRange(this.last,  this.lastActive+1);
18738                     this.grid.getView().focusRow(this.lastActive);
18739                     if(last !== false){
18740                         this.last = last;
18741                     }
18742                 }else{
18743                     this.selectFirstRow();
18744                 }
18745                 this.fireEvent("afterselectionchange", this);
18746             },
18747             scope: this
18748         });
18749
18750         var view = this.grid.view;
18751         view.on("refresh", this.onRefresh, this);
18752         view.on("rowupdated", this.onRowUpdated, this);
18753         view.on("rowremoved", this.onRemove, this);
18754     },
18755
18756     // private
18757     onRefresh : function(){
18758         var ds = this.grid.dataSource, i, v = this.grid.view;
18759         var s = this.selections;
18760         s.each(function(r){
18761             if((i = ds.indexOfId(r.id)) != -1){
18762                 v.onRowSelect(i);
18763             }else{
18764                 s.remove(r);
18765             }
18766         });
18767     },
18768
18769     // private
18770     onRemove : function(v, index, r){
18771         this.selections.remove(r);
18772     },
18773
18774     // private
18775     onRowUpdated : function(v, index, r){
18776         if(this.isSelected(r)){
18777             v.onRowSelect(index);
18778         }
18779     },
18780
18781     /**
18782      * Select records.
18783      * @param {Array} records The records to select
18784      * @param {Boolean} keepExisting (optional) True to keep existing selections
18785      */
18786     selectRecords : function(records, keepExisting){
18787         if(!keepExisting){
18788             this.clearSelections();
18789         }
18790         var ds = this.grid.dataSource;
18791         for(var i = 0, len = records.length; i < len; i++){
18792             this.selectRow(ds.indexOf(records[i]), true);
18793         }
18794     },
18795
18796     /**
18797      * Gets the number of selected rows.
18798      * @return {Number}
18799      */
18800     getCount : function(){
18801         return this.selections.length;
18802     },
18803
18804     /**
18805      * Selects the first row in the grid.
18806      */
18807     selectFirstRow : function(){
18808         this.selectRow(0);
18809     },
18810
18811     /**
18812      * Select the last row.
18813      * @param {Boolean} keepExisting (optional) True to keep existing selections
18814      */
18815     selectLastRow : function(keepExisting){
18816         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18817     },
18818
18819     /**
18820      * Selects the row immediately following the last selected row.
18821      * @param {Boolean} keepExisting (optional) True to keep existing selections
18822      */
18823     selectNext : function(keepExisting){
18824         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18825             this.selectRow(this.last+1, keepExisting);
18826             this.grid.getView().focusRow(this.last);
18827         }
18828     },
18829
18830     /**
18831      * Selects the row that precedes the last selected row.
18832      * @param {Boolean} keepExisting (optional) True to keep existing selections
18833      */
18834     selectPrevious : function(keepExisting){
18835         if(this.last){
18836             this.selectRow(this.last-1, keepExisting);
18837             this.grid.getView().focusRow(this.last);
18838         }
18839     },
18840
18841     /**
18842      * Returns the selected records
18843      * @return {Array} Array of selected records
18844      */
18845     getSelections : function(){
18846         return [].concat(this.selections.items);
18847     },
18848
18849     /**
18850      * Returns the first selected record.
18851      * @return {Record}
18852      */
18853     getSelected : function(){
18854         return this.selections.itemAt(0);
18855     },
18856
18857
18858     /**
18859      * Clears all selections.
18860      */
18861     clearSelections : function(fast){
18862         if(this.locked) return;
18863         if(fast !== true){
18864             var ds = this.grid.dataSource;
18865             var s = this.selections;
18866             s.each(function(r){
18867                 this.deselectRow(ds.indexOfId(r.id));
18868             }, this);
18869             s.clear();
18870         }else{
18871             this.selections.clear();
18872         }
18873         this.last = false;
18874     },
18875
18876
18877     /**
18878      * Selects all rows.
18879      */
18880     selectAll : function(){
18881         if(this.locked) return;
18882         this.selections.clear();
18883         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18884             this.selectRow(i, true);
18885         }
18886     },
18887
18888     /**
18889      * Returns True if there is a selection.
18890      * @return {Boolean}
18891      */
18892     hasSelection : function(){
18893         return this.selections.length > 0;
18894     },
18895
18896     /**
18897      * Returns True if the specified row is selected.
18898      * @param {Number/Record} record The record or index of the record to check
18899      * @return {Boolean}
18900      */
18901     isSelected : function(index){
18902         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18903         return (r && this.selections.key(r.id) ? true : false);
18904     },
18905
18906     /**
18907      * Returns True if the specified record id is selected.
18908      * @param {String} id The id of record to check
18909      * @return {Boolean}
18910      */
18911     isIdSelected : function(id){
18912         return (this.selections.key(id) ? true : false);
18913     },
18914
18915     // private
18916     handleMouseDown : function(e, t){
18917         var view = this.grid.getView(), rowIndex;
18918         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18919             return;
18920         };
18921         if(e.shiftKey && this.last !== false){
18922             var last = this.last;
18923             this.selectRange(last, rowIndex, e.ctrlKey);
18924             this.last = last; // reset the last
18925             view.focusRow(rowIndex);
18926         }else{
18927             var isSelected = this.isSelected(rowIndex);
18928             if(e.button !== 0 && isSelected){
18929                 view.focusRow(rowIndex);
18930             }else if(e.ctrlKey && isSelected){
18931                 this.deselectRow(rowIndex);
18932             }else if(!isSelected){
18933                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18934                 view.focusRow(rowIndex);
18935             }
18936         }
18937         this.fireEvent("afterselectionchange", this);
18938     },
18939     // private
18940     handleDragableRowClick :  function(grid, rowIndex, e) 
18941     {
18942         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18943             this.selectRow(rowIndex, false);
18944             grid.view.focusRow(rowIndex);
18945              this.fireEvent("afterselectionchange", this);
18946         }
18947     },
18948     
18949     /**
18950      * Selects multiple rows.
18951      * @param {Array} rows Array of the indexes of the row to select
18952      * @param {Boolean} keepExisting (optional) True to keep existing selections
18953      */
18954     selectRows : function(rows, keepExisting){
18955         if(!keepExisting){
18956             this.clearSelections();
18957         }
18958         for(var i = 0, len = rows.length; i < len; i++){
18959             this.selectRow(rows[i], true);
18960         }
18961     },
18962
18963     /**
18964      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18965      * @param {Number} startRow The index of the first row in the range
18966      * @param {Number} endRow The index of the last row in the range
18967      * @param {Boolean} keepExisting (optional) True to retain existing selections
18968      */
18969     selectRange : function(startRow, endRow, keepExisting){
18970         if(this.locked) return;
18971         if(!keepExisting){
18972             this.clearSelections();
18973         }
18974         if(startRow <= endRow){
18975             for(var i = startRow; i <= endRow; i++){
18976                 this.selectRow(i, true);
18977             }
18978         }else{
18979             for(var i = startRow; i >= endRow; i--){
18980                 this.selectRow(i, true);
18981             }
18982         }
18983     },
18984
18985     /**
18986      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18987      * @param {Number} startRow The index of the first row in the range
18988      * @param {Number} endRow The index of the last row in the range
18989      */
18990     deselectRange : function(startRow, endRow, preventViewNotify){
18991         if(this.locked) return;
18992         for(var i = startRow; i <= endRow; i++){
18993             this.deselectRow(i, preventViewNotify);
18994         }
18995     },
18996
18997     /**
18998      * Selects a row.
18999      * @param {Number} row The index of the row to select
19000      * @param {Boolean} keepExisting (optional) True to keep existing selections
19001      */
19002     selectRow : function(index, keepExisting, preventViewNotify){
19003         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19004         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19005             if(!keepExisting || this.singleSelect){
19006                 this.clearSelections();
19007             }
19008             var r = this.grid.dataSource.getAt(index);
19009             this.selections.add(r);
19010             this.last = this.lastActive = index;
19011             if(!preventViewNotify){
19012                 this.grid.getView().onRowSelect(index);
19013             }
19014             this.fireEvent("rowselect", this, index, r);
19015             this.fireEvent("selectionchange", this);
19016         }
19017     },
19018
19019     /**
19020      * Deselects a row.
19021      * @param {Number} row The index of the row to deselect
19022      */
19023     deselectRow : function(index, preventViewNotify){
19024         if(this.locked) return;
19025         if(this.last == index){
19026             this.last = false;
19027         }
19028         if(this.lastActive == index){
19029             this.lastActive = false;
19030         }
19031         var r = this.grid.dataSource.getAt(index);
19032         this.selections.remove(r);
19033         if(!preventViewNotify){
19034             this.grid.getView().onRowDeselect(index);
19035         }
19036         this.fireEvent("rowdeselect", this, index);
19037         this.fireEvent("selectionchange", this);
19038     },
19039
19040     // private
19041     restoreLast : function(){
19042         if(this._last){
19043             this.last = this._last;
19044         }
19045     },
19046
19047     // private
19048     acceptsNav : function(row, col, cm){
19049         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19050     },
19051
19052     // private
19053     onEditorKey : function(field, e){
19054         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19055         if(k == e.TAB){
19056             e.stopEvent();
19057             ed.completeEdit();
19058             if(e.shiftKey){
19059                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19060             }else{
19061                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19062             }
19063         }else if(k == e.ENTER && !e.ctrlKey){
19064             e.stopEvent();
19065             ed.completeEdit();
19066             if(e.shiftKey){
19067                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19068             }else{
19069                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19070             }
19071         }else if(k == e.ESC){
19072             ed.cancelEdit();
19073         }
19074         if(newCell){
19075             g.startEditing(newCell[0], newCell[1]);
19076         }
19077     }
19078 });/*
19079  * Based on:
19080  * Ext JS Library 1.1.1
19081  * Copyright(c) 2006-2007, Ext JS, LLC.
19082  *
19083  * Originally Released Under LGPL - original licence link has changed is not relivant.
19084  *
19085  * Fork - LGPL
19086  * <script type="text/javascript">
19087  */
19088  
19089 /**
19090  * @class Roo.bootstrap.PagingToolbar
19091  * @extends Roo.Row
19092  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19093  * @constructor
19094  * Create a new PagingToolbar
19095  * @param {Object} config The config object
19096  */
19097 Roo.bootstrap.PagingToolbar = function(config)
19098 {
19099     // old args format still supported... - xtype is prefered..
19100         // created from xtype...
19101     var ds = config.dataSource;
19102     this.toolbarItems = [];
19103     if (config.items) {
19104         this.toolbarItems = config.items;
19105 //        config.items = [];
19106     }
19107     
19108     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19109     this.ds = ds;
19110     this.cursor = 0;
19111     if (ds) { 
19112         this.bind(ds);
19113     }
19114     
19115     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19116     
19117 };
19118
19119 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19120     /**
19121      * @cfg {Roo.data.Store} dataSource
19122      * The underlying data store providing the paged data
19123      */
19124     /**
19125      * @cfg {String/HTMLElement/Element} container
19126      * container The id or element that will contain the toolbar
19127      */
19128     /**
19129      * @cfg {Boolean} displayInfo
19130      * True to display the displayMsg (defaults to false)
19131      */
19132     /**
19133      * @cfg {Number} pageSize
19134      * The number of records to display per page (defaults to 20)
19135      */
19136     pageSize: 20,
19137     /**
19138      * @cfg {String} displayMsg
19139      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19140      */
19141     displayMsg : 'Displaying {0} - {1} of {2}',
19142     /**
19143      * @cfg {String} emptyMsg
19144      * The message to display when no records are found (defaults to "No data to display")
19145      */
19146     emptyMsg : 'No data to display',
19147     /**
19148      * Customizable piece of the default paging text (defaults to "Page")
19149      * @type String
19150      */
19151     beforePageText : "Page",
19152     /**
19153      * Customizable piece of the default paging text (defaults to "of %0")
19154      * @type String
19155      */
19156     afterPageText : "of {0}",
19157     /**
19158      * Customizable piece of the default paging text (defaults to "First Page")
19159      * @type String
19160      */
19161     firstText : "First Page",
19162     /**
19163      * Customizable piece of the default paging text (defaults to "Previous Page")
19164      * @type String
19165      */
19166     prevText : "Previous Page",
19167     /**
19168      * Customizable piece of the default paging text (defaults to "Next Page")
19169      * @type String
19170      */
19171     nextText : "Next Page",
19172     /**
19173      * Customizable piece of the default paging text (defaults to "Last Page")
19174      * @type String
19175      */
19176     lastText : "Last Page",
19177     /**
19178      * Customizable piece of the default paging text (defaults to "Refresh")
19179      * @type String
19180      */
19181     refreshText : "Refresh",
19182
19183     buttons : false,
19184     // private
19185     onRender : function(ct, position) 
19186     {
19187         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19188         this.navgroup.parentId = this.id;
19189         this.navgroup.onRender(this.el, null);
19190         // add the buttons to the navgroup
19191         
19192         if(this.displayInfo){
19193             Roo.log(this.el.select('ul.navbar-nav',true).first());
19194             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19195             this.displayEl = this.el.select('.x-paging-info', true).first();
19196 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19197 //            this.displayEl = navel.el.select('span',true).first();
19198         }
19199         
19200         var _this = this;
19201         
19202         if(this.buttons){
19203             Roo.each(_this.buttons, function(e){
19204                Roo.factory(e).onRender(_this.el, null);
19205             });
19206         }
19207             
19208         Roo.each(_this.toolbarItems, function(e) {
19209             _this.navgroup.addItem(e);
19210         });
19211         
19212         this.first = this.navgroup.addItem({
19213             tooltip: this.firstText,
19214             cls: "prev",
19215             icon : 'fa fa-backward',
19216             disabled: true,
19217             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19218         });
19219         
19220         this.prev =  this.navgroup.addItem({
19221             tooltip: this.prevText,
19222             cls: "prev",
19223             icon : 'fa fa-step-backward',
19224             disabled: true,
19225             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19226         });
19227     //this.addSeparator();
19228         
19229         
19230         var field = this.navgroup.addItem( {
19231             tagtype : 'span',
19232             cls : 'x-paging-position',
19233             
19234             html : this.beforePageText  +
19235                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19236                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19237          } ); //?? escaped?
19238         
19239         this.field = field.el.select('input', true).first();
19240         this.field.on("keydown", this.onPagingKeydown, this);
19241         this.field.on("focus", function(){this.dom.select();});
19242     
19243     
19244         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19245         //this.field.setHeight(18);
19246         //this.addSeparator();
19247         this.next = this.navgroup.addItem({
19248             tooltip: this.nextText,
19249             cls: "next",
19250             html : ' <i class="fa fa-step-forward">',
19251             disabled: true,
19252             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19253         });
19254         this.last = this.navgroup.addItem({
19255             tooltip: this.lastText,
19256             icon : 'fa fa-forward',
19257             cls: "next",
19258             disabled: true,
19259             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19260         });
19261     //this.addSeparator();
19262         this.loading = this.navgroup.addItem({
19263             tooltip: this.refreshText,
19264             icon: 'fa fa-refresh',
19265             
19266             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19267         });
19268
19269     },
19270
19271     // private
19272     updateInfo : function(){
19273         if(this.displayEl){
19274             var count = this.ds.getCount();
19275             var msg = count == 0 ?
19276                 this.emptyMsg :
19277                 String.format(
19278                     this.displayMsg,
19279                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19280                 );
19281             this.displayEl.update(msg);
19282         }
19283     },
19284
19285     // private
19286     onLoad : function(ds, r, o){
19287        this.cursor = o.params ? o.params.start : 0;
19288        var d = this.getPageData(),
19289             ap = d.activePage,
19290             ps = d.pages;
19291         
19292        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19293        this.field.dom.value = ap;
19294        this.first.setDisabled(ap == 1);
19295        this.prev.setDisabled(ap == 1);
19296        this.next.setDisabled(ap == ps);
19297        this.last.setDisabled(ap == ps);
19298        this.loading.enable();
19299        this.updateInfo();
19300     },
19301
19302     // private
19303     getPageData : function(){
19304         var total = this.ds.getTotalCount();
19305         return {
19306             total : total,
19307             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19308             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19309         };
19310     },
19311
19312     // private
19313     onLoadError : function(){
19314         this.loading.enable();
19315     },
19316
19317     // private
19318     onPagingKeydown : function(e){
19319         var k = e.getKey();
19320         var d = this.getPageData();
19321         if(k == e.RETURN){
19322             var v = this.field.dom.value, pageNum;
19323             if(!v || isNaN(pageNum = parseInt(v, 10))){
19324                 this.field.dom.value = d.activePage;
19325                 return;
19326             }
19327             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19328             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19329             e.stopEvent();
19330         }
19331         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))
19332         {
19333           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19334           this.field.dom.value = pageNum;
19335           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19336           e.stopEvent();
19337         }
19338         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19339         {
19340           var v = this.field.dom.value, pageNum; 
19341           var increment = (e.shiftKey) ? 10 : 1;
19342           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19343             increment *= -1;
19344           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19345             this.field.dom.value = d.activePage;
19346             return;
19347           }
19348           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19349           {
19350             this.field.dom.value = parseInt(v, 10) + increment;
19351             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19352             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19353           }
19354           e.stopEvent();
19355         }
19356     },
19357
19358     // private
19359     beforeLoad : function(){
19360         if(this.loading){
19361             this.loading.disable();
19362         }
19363     },
19364
19365     // private
19366     onClick : function(which){
19367         var ds = this.ds;
19368         if (!ds) {
19369             return;
19370         }
19371         switch(which){
19372             case "first":
19373                 ds.load({params:{start: 0, limit: this.pageSize}});
19374             break;
19375             case "prev":
19376                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19377             break;
19378             case "next":
19379                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19380             break;
19381             case "last":
19382                 var total = ds.getTotalCount();
19383                 var extra = total % this.pageSize;
19384                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19385                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19386             break;
19387             case "refresh":
19388                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19389             break;
19390         }
19391     },
19392
19393     /**
19394      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19395      * @param {Roo.data.Store} store The data store to unbind
19396      */
19397     unbind : function(ds){
19398         ds.un("beforeload", this.beforeLoad, this);
19399         ds.un("load", this.onLoad, this);
19400         ds.un("loadexception", this.onLoadError, this);
19401         ds.un("remove", this.updateInfo, this);
19402         ds.un("add", this.updateInfo, this);
19403         this.ds = undefined;
19404     },
19405
19406     /**
19407      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19408      * @param {Roo.data.Store} store The data store to bind
19409      */
19410     bind : function(ds){
19411         ds.on("beforeload", this.beforeLoad, this);
19412         ds.on("load", this.onLoad, this);
19413         ds.on("loadexception", this.onLoadError, this);
19414         ds.on("remove", this.updateInfo, this);
19415         ds.on("add", this.updateInfo, this);
19416         this.ds = ds;
19417     }
19418 });/*
19419  * - LGPL
19420  *
19421  * element
19422  * 
19423  */
19424
19425 /**
19426  * @class Roo.bootstrap.MessageBar
19427  * @extends Roo.bootstrap.Component
19428  * Bootstrap MessageBar class
19429  * @cfg {String} html contents of the MessageBar
19430  * @cfg {String} weight (info | success | warning | danger) default info
19431  * @cfg {String} beforeClass insert the bar before the given class
19432  * @cfg {Boolean} closable (true | false) default false
19433  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19434  * 
19435  * @constructor
19436  * Create a new Element
19437  * @param {Object} config The config object
19438  */
19439
19440 Roo.bootstrap.MessageBar = function(config){
19441     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19442 };
19443
19444 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19445     
19446     html: '',
19447     weight: 'info',
19448     closable: false,
19449     fixed: false,
19450     beforeClass: 'bootstrap-sticky-wrap',
19451     
19452     getAutoCreate : function(){
19453         
19454         var cfg = {
19455             tag: 'div',
19456             cls: 'alert alert-dismissable alert-' + this.weight,
19457             cn: [
19458                 {
19459                     tag: 'span',
19460                     cls: 'message',
19461                     html: this.html || ''
19462                 }
19463             ]
19464         }
19465         
19466         if(this.fixed){
19467             cfg.cls += ' alert-messages-fixed';
19468         }
19469         
19470         if(this.closable){
19471             cfg.cn.push({
19472                 tag: 'button',
19473                 cls: 'close',
19474                 html: 'x'
19475             });
19476         }
19477         
19478         return cfg;
19479     },
19480     
19481     onRender : function(ct, position)
19482     {
19483         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19484         
19485         if(!this.el){
19486             var cfg = Roo.apply({},  this.getAutoCreate());
19487             cfg.id = Roo.id();
19488             
19489             if (this.cls) {
19490                 cfg.cls += ' ' + this.cls;
19491             }
19492             if (this.style) {
19493                 cfg.style = this.style;
19494             }
19495             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19496             
19497             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19498         }
19499         
19500         this.el.select('>button.close').on('click', this.hide, this);
19501         
19502     },
19503     
19504     show : function()
19505     {
19506         if (!this.rendered) {
19507             this.render();
19508         }
19509         
19510         this.el.show();
19511         
19512         this.fireEvent('show', this);
19513         
19514     },
19515     
19516     hide : function()
19517     {
19518         if (!this.rendered) {
19519             this.render();
19520         }
19521         
19522         this.el.hide();
19523         
19524         this.fireEvent('hide', this);
19525     },
19526     
19527     update : function()
19528     {
19529 //        var e = this.el.dom.firstChild;
19530 //        
19531 //        if(this.closable){
19532 //            e = e.nextSibling;
19533 //        }
19534 //        
19535 //        e.data = this.html || '';
19536
19537         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19538     }
19539    
19540 });
19541
19542  
19543
19544      /*
19545  * - LGPL
19546  *
19547  * Graph
19548  * 
19549  */
19550
19551
19552 /**
19553  * @class Roo.bootstrap.Graph
19554  * @extends Roo.bootstrap.Component
19555  * Bootstrap Graph class
19556 > Prameters
19557  -sm {number} sm 4
19558  -md {number} md 5
19559  @cfg {String} graphtype  bar | vbar | pie
19560  @cfg {number} g_x coodinator | centre x (pie)
19561  @cfg {number} g_y coodinator | centre y (pie)
19562  @cfg {number} g_r radius (pie)
19563  @cfg {number} g_height height of the chart (respected by all elements in the set)
19564  @cfg {number} g_width width of the chart (respected by all elements in the set)
19565  @cfg {Object} title The title of the chart
19566     
19567  -{Array}  values
19568  -opts (object) options for the chart 
19569      o {
19570      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19571      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19572      o vgutter (number)
19573      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.
19574      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19575      o to
19576      o stretch (boolean)
19577      o }
19578  -opts (object) options for the pie
19579      o{
19580      o cut
19581      o startAngle (number)
19582      o endAngle (number)
19583      } 
19584  *
19585  * @constructor
19586  * Create a new Input
19587  * @param {Object} config The config object
19588  */
19589
19590 Roo.bootstrap.Graph = function(config){
19591     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19592     
19593     this.addEvents({
19594         // img events
19595         /**
19596          * @event click
19597          * The img click event for the img.
19598          * @param {Roo.EventObject} e
19599          */
19600         "click" : true
19601     });
19602 };
19603
19604 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19605     
19606     sm: 4,
19607     md: 5,
19608     graphtype: 'bar',
19609     g_height: 250,
19610     g_width: 400,
19611     g_x: 50,
19612     g_y: 50,
19613     g_r: 30,
19614     opts:{
19615         //g_colors: this.colors,
19616         g_type: 'soft',
19617         g_gutter: '20%'
19618
19619     },
19620     title : false,
19621
19622     getAutoCreate : function(){
19623         
19624         var cfg = {
19625             tag: 'div',
19626             html : null
19627         }
19628         
19629         
19630         return  cfg;
19631     },
19632
19633     onRender : function(ct,position){
19634         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19635         this.raphael = Raphael(this.el.dom);
19636         
19637                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19638                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19639                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19640                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19641                 /*
19642                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19643                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19644                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19645                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19646                 
19647                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19648                 r.barchart(330, 10, 300, 220, data1);
19649                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19650                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19651                 */
19652                 
19653                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19654                 // r.barchart(30, 30, 560, 250,  xdata, {
19655                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19656                 //     axis : "0 0 1 1",
19657                 //     axisxlabels :  xdata
19658                 //     //yvalues : cols,
19659                    
19660                 // });
19661 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19662 //        
19663 //        this.load(null,xdata,{
19664 //                axis : "0 0 1 1",
19665 //                axisxlabels :  xdata
19666 //                });
19667
19668     },
19669
19670     load : function(graphtype,xdata,opts){
19671         this.raphael.clear();
19672         if(!graphtype) {
19673             graphtype = this.graphtype;
19674         }
19675         if(!opts){
19676             opts = this.opts;
19677         }
19678         var r = this.raphael,
19679             fin = function () {
19680                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19681             },
19682             fout = function () {
19683                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19684             },
19685             pfin = function() {
19686                 this.sector.stop();
19687                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19688
19689                 if (this.label) {
19690                     this.label[0].stop();
19691                     this.label[0].attr({ r: 7.5 });
19692                     this.label[1].attr({ "font-weight": 800 });
19693                 }
19694             },
19695             pfout = function() {
19696                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19697
19698                 if (this.label) {
19699                     this.label[0].animate({ r: 5 }, 500, "bounce");
19700                     this.label[1].attr({ "font-weight": 400 });
19701                 }
19702             };
19703
19704         switch(graphtype){
19705             case 'bar':
19706                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19707                 break;
19708             case 'hbar':
19709                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19710                 break;
19711             case 'pie':
19712 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19713 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19714 //            
19715                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19716                 
19717                 break;
19718
19719         }
19720         
19721         if(this.title){
19722             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19723         }
19724         
19725     },
19726     
19727     setTitle: function(o)
19728     {
19729         this.title = o;
19730     },
19731     
19732     initEvents: function() {
19733         
19734         if(!this.href){
19735             this.el.on('click', this.onClick, this);
19736         }
19737     },
19738     
19739     onClick : function(e)
19740     {
19741         Roo.log('img onclick');
19742         this.fireEvent('click', this, e);
19743     }
19744    
19745 });
19746
19747  
19748 /*
19749  * - LGPL
19750  *
19751  * numberBox
19752  * 
19753  */
19754 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19755
19756 /**
19757  * @class Roo.bootstrap.dash.NumberBox
19758  * @extends Roo.bootstrap.Component
19759  * Bootstrap NumberBox class
19760  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19761  * @cfg {String} headline Box headline
19762  * @cfg {String} content Box content
19763  * @cfg {String} icon Box icon
19764  * @cfg {String} footer Footer text
19765  * @cfg {String} fhref Footer href
19766  * 
19767  * @constructor
19768  * Create a new NumberBox
19769  * @param {Object} config The config object
19770  */
19771
19772
19773 Roo.bootstrap.dash.NumberBox = function(config){
19774     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19775     
19776 };
19777
19778 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19779     
19780     bgcolor : 'aqua',
19781     headline : '',
19782     content : '',
19783     icon : '',
19784     footer : '',
19785     fhref : '',
19786     ficon : '',
19787     
19788     getAutoCreate : function(){
19789         
19790         var cfg = {
19791             tag : 'div',
19792             cls : 'small-box bg-' + this.bgcolor,
19793             cn : [
19794                 {
19795                     tag : 'div',
19796                     cls : 'inner',
19797                     cn :[
19798                         {
19799                             tag : 'h3',
19800                             cls : 'roo-headline',
19801                             html : this.headline
19802                         },
19803                         {
19804                             tag : 'p',
19805                             cls : 'roo-content',
19806                             html : this.content
19807                         }
19808                     ]
19809                 }
19810             ]
19811         }
19812         
19813         if(this.icon){
19814             cfg.cn.push({
19815                 tag : 'div',
19816                 cls : 'icon',
19817                 cn :[
19818                     {
19819                         tag : 'i',
19820                         cls : 'ion ' + this.icon
19821                     }
19822                 ]
19823             });
19824         }
19825         
19826         if(this.footer){
19827             var footer = {
19828                 tag : 'a',
19829                 cls : 'small-box-footer',
19830                 href : this.fhref || '#',
19831                 html : this.footer
19832             };
19833             
19834             cfg.cn.push(footer);
19835             
19836         }
19837         
19838         return  cfg;
19839     },
19840
19841     onRender : function(ct,position){
19842         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19843
19844
19845        
19846                 
19847     },
19848
19849     setHeadline: function (value)
19850     {
19851         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19852     },
19853     
19854     setFooter: function (value, href)
19855     {
19856         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19857         
19858         if(href){
19859             this.el.select('a.small-box-footer',true).first().attr('href', href);
19860         }
19861         
19862     },
19863
19864     setContent: function (value)
19865     {
19866         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19867     },
19868
19869     initEvents: function() 
19870     {   
19871         
19872     }
19873     
19874 });
19875
19876  
19877 /*
19878  * - LGPL
19879  *
19880  * TabBox
19881  * 
19882  */
19883 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19884
19885 /**
19886  * @class Roo.bootstrap.dash.TabBox
19887  * @extends Roo.bootstrap.Component
19888  * Bootstrap TabBox class
19889  * @cfg {String} title Title of the TabBox
19890  * @cfg {String} icon Icon of the TabBox
19891  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19892  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19893  * 
19894  * @constructor
19895  * Create a new TabBox
19896  * @param {Object} config The config object
19897  */
19898
19899
19900 Roo.bootstrap.dash.TabBox = function(config){
19901     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19902     this.addEvents({
19903         // raw events
19904         /**
19905          * @event addpane
19906          * When a pane is added
19907          * @param {Roo.bootstrap.dash.TabPane} pane
19908          */
19909         "addpane" : true,
19910         /**
19911          * @event activatepane
19912          * When a pane is activated
19913          * @param {Roo.bootstrap.dash.TabPane} pane
19914          */
19915         "activatepane" : true
19916         
19917          
19918     });
19919     
19920     this.panes = [];
19921 };
19922
19923 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19924
19925     title : '',
19926     icon : false,
19927     showtabs : true,
19928     tabScrollable : false,
19929     
19930     getChildContainer : function()
19931     {
19932         return this.el.select('.tab-content', true).first();
19933     },
19934     
19935     getAutoCreate : function(){
19936         
19937         var header = {
19938             tag: 'li',
19939             cls: 'pull-left header',
19940             html: this.title,
19941             cn : []
19942         };
19943         
19944         if(this.icon){
19945             header.cn.push({
19946                 tag: 'i',
19947                 cls: 'fa ' + this.icon
19948             });
19949         }
19950         
19951         var h = {
19952             tag: 'ul',
19953             cls: 'nav nav-tabs pull-right',
19954             cn: [
19955                 header
19956             ]
19957         };
19958         
19959         if(this.tabScrollable){
19960             h = {
19961                 tag: 'div',
19962                 cls: 'tab-header',
19963                 cn: [
19964                     {
19965                         tag: 'ul',
19966                         cls: 'nav nav-tabs pull-right',
19967                         cn: [
19968                             header
19969                         ]
19970                     }
19971                 ]
19972             }
19973         }
19974         
19975         var cfg = {
19976             tag: 'div',
19977             cls: 'nav-tabs-custom',
19978             cn: [
19979                 h,
19980                 {
19981                     tag: 'div',
19982                     cls: 'tab-content no-padding',
19983                     cn: []
19984                 }
19985             ]
19986         }
19987
19988         return  cfg;
19989     },
19990     initEvents : function()
19991     {
19992         //Roo.log('add add pane handler');
19993         this.on('addpane', this.onAddPane, this);
19994     },
19995      /**
19996      * Updates the box title
19997      * @param {String} html to set the title to.
19998      */
19999     setTitle : function(value)
20000     {
20001         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20002     },
20003     onAddPane : function(pane)
20004     {
20005         this.panes.push(pane);
20006         //Roo.log('addpane');
20007         //Roo.log(pane);
20008         // tabs are rendere left to right..
20009         if(!this.showtabs){
20010             return;
20011         }
20012         
20013         var ctr = this.el.select('.nav-tabs', true).first();
20014          
20015          
20016         var existing = ctr.select('.nav-tab',true);
20017         var qty = existing.getCount();;
20018         
20019         
20020         var tab = ctr.createChild({
20021             tag : 'li',
20022             cls : 'nav-tab' + (qty ? '' : ' active'),
20023             cn : [
20024                 {
20025                     tag : 'a',
20026                     href:'#',
20027                     html : pane.title
20028                 }
20029             ]
20030         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20031         pane.tab = tab;
20032         
20033         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20034         if (!qty) {
20035             pane.el.addClass('active');
20036         }
20037         
20038                 
20039     },
20040     onTabClick : function(ev,un,ob,pane)
20041     {
20042         //Roo.log('tab - prev default');
20043         ev.preventDefault();
20044         
20045         
20046         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20047         pane.tab.addClass('active');
20048         //Roo.log(pane.title);
20049         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20050         // technically we should have a deactivate event.. but maybe add later.
20051         // and it should not de-activate the selected tab...
20052         this.fireEvent('activatepane', pane);
20053         pane.el.addClass('active');
20054         pane.fireEvent('activate');
20055         
20056         
20057     },
20058     
20059     getActivePane : function()
20060     {
20061         var r = false;
20062         Roo.each(this.panes, function(p) {
20063             if(p.el.hasClass('active')){
20064                 r = p;
20065                 return false;
20066             }
20067             
20068             return;
20069         });
20070         
20071         return r;
20072     }
20073     
20074     
20075 });
20076
20077  
20078 /*
20079  * - LGPL
20080  *
20081  * Tab pane
20082  * 
20083  */
20084 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20085 /**
20086  * @class Roo.bootstrap.TabPane
20087  * @extends Roo.bootstrap.Component
20088  * Bootstrap TabPane class
20089  * @cfg {Boolean} active (false | true) Default false
20090  * @cfg {String} title title of panel
20091
20092  * 
20093  * @constructor
20094  * Create a new TabPane
20095  * @param {Object} config The config object
20096  */
20097
20098 Roo.bootstrap.dash.TabPane = function(config){
20099     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20100     
20101     this.addEvents({
20102         // raw events
20103         /**
20104          * @event activate
20105          * When a pane is activated
20106          * @param {Roo.bootstrap.dash.TabPane} pane
20107          */
20108         "activate" : true
20109          
20110     });
20111 };
20112
20113 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20114     
20115     active : false,
20116     title : '',
20117     
20118     // the tabBox that this is attached to.
20119     tab : false,
20120      
20121     getAutoCreate : function() 
20122     {
20123         var cfg = {
20124             tag: 'div',
20125             cls: 'tab-pane'
20126         }
20127         
20128         if(this.active){
20129             cfg.cls += ' active';
20130         }
20131         
20132         return cfg;
20133     },
20134     initEvents  : function()
20135     {
20136         //Roo.log('trigger add pane handler');
20137         this.parent().fireEvent('addpane', this)
20138     },
20139     
20140      /**
20141      * Updates the tab title 
20142      * @param {String} html to set the title to.
20143      */
20144     setTitle: function(str)
20145     {
20146         if (!this.tab) {
20147             return;
20148         }
20149         this.title = str;
20150         this.tab.select('a', true).first().dom.innerHTML = str;
20151         
20152     }
20153     
20154     
20155     
20156 });
20157
20158  
20159
20160
20161  /*
20162  * - LGPL
20163  *
20164  * menu
20165  * 
20166  */
20167 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20168
20169 /**
20170  * @class Roo.bootstrap.menu.Menu
20171  * @extends Roo.bootstrap.Component
20172  * Bootstrap Menu class - container for Menu
20173  * @cfg {String} html Text of the menu
20174  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20175  * @cfg {String} icon Font awesome icon
20176  * @cfg {String} pos Menu align to (top | bottom) default bottom
20177  * 
20178  * 
20179  * @constructor
20180  * Create a new Menu
20181  * @param {Object} config The config object
20182  */
20183
20184
20185 Roo.bootstrap.menu.Menu = function(config){
20186     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20187     
20188     this.addEvents({
20189         /**
20190          * @event beforeshow
20191          * Fires before this menu is displayed
20192          * @param {Roo.bootstrap.menu.Menu} this
20193          */
20194         beforeshow : true,
20195         /**
20196          * @event beforehide
20197          * Fires before this menu is hidden
20198          * @param {Roo.bootstrap.menu.Menu} this
20199          */
20200         beforehide : true,
20201         /**
20202          * @event show
20203          * Fires after this menu is displayed
20204          * @param {Roo.bootstrap.menu.Menu} this
20205          */
20206         show : true,
20207         /**
20208          * @event hide
20209          * Fires after this menu is hidden
20210          * @param {Roo.bootstrap.menu.Menu} this
20211          */
20212         hide : true,
20213         /**
20214          * @event click
20215          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20216          * @param {Roo.bootstrap.menu.Menu} this
20217          * @param {Roo.EventObject} e
20218          */
20219         click : true
20220     });
20221     
20222 };
20223
20224 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20225     
20226     submenu : false,
20227     html : '',
20228     weight : 'default',
20229     icon : false,
20230     pos : 'bottom',
20231     
20232     
20233     getChildContainer : function() {
20234         if(this.isSubMenu){
20235             return this.el;
20236         }
20237         
20238         return this.el.select('ul.dropdown-menu', true).first();  
20239     },
20240     
20241     getAutoCreate : function()
20242     {
20243         var text = [
20244             {
20245                 tag : 'span',
20246                 cls : 'roo-menu-text',
20247                 html : this.html
20248             }
20249         ];
20250         
20251         if(this.icon){
20252             text.unshift({
20253                 tag : 'i',
20254                 cls : 'fa ' + this.icon
20255             })
20256         }
20257         
20258         
20259         var cfg = {
20260             tag : 'div',
20261             cls : 'btn-group',
20262             cn : [
20263                 {
20264                     tag : 'button',
20265                     cls : 'dropdown-button btn btn-' + this.weight,
20266                     cn : text
20267                 },
20268                 {
20269                     tag : 'button',
20270                     cls : 'dropdown-toggle btn btn-' + this.weight,
20271                     cn : [
20272                         {
20273                             tag : 'span',
20274                             cls : 'caret'
20275                         }
20276                     ]
20277                 },
20278                 {
20279                     tag : 'ul',
20280                     cls : 'dropdown-menu'
20281                 }
20282             ]
20283             
20284         };
20285         
20286         if(this.pos == 'top'){
20287             cfg.cls += ' dropup';
20288         }
20289         
20290         if(this.isSubMenu){
20291             cfg = {
20292                 tag : 'ul',
20293                 cls : 'dropdown-menu'
20294             }
20295         }
20296         
20297         return cfg;
20298     },
20299     
20300     onRender : function(ct, position)
20301     {
20302         this.isSubMenu = ct.hasClass('dropdown-submenu');
20303         
20304         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20305     },
20306     
20307     initEvents : function() 
20308     {
20309         if(this.isSubMenu){
20310             return;
20311         }
20312         
20313         this.hidden = true;
20314         
20315         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20316         this.triggerEl.on('click', this.onTriggerPress, this);
20317         
20318         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20319         this.buttonEl.on('click', this.onClick, this);
20320         
20321     },
20322     
20323     list : function()
20324     {
20325         if(this.isSubMenu){
20326             return this.el;
20327         }
20328         
20329         return this.el.select('ul.dropdown-menu', true).first();
20330     },
20331     
20332     onClick : function(e)
20333     {
20334         this.fireEvent("click", this, e);
20335     },
20336     
20337     onTriggerPress  : function(e)
20338     {   
20339         if (this.isVisible()) {
20340             this.hide();
20341         } else {
20342             this.show();
20343         }
20344     },
20345     
20346     isVisible : function(){
20347         return !this.hidden;
20348     },
20349     
20350     show : function()
20351     {
20352         this.fireEvent("beforeshow", this);
20353         
20354         this.hidden = false;
20355         this.el.addClass('open');
20356         
20357         Roo.get(document).on("mouseup", this.onMouseUp, this);
20358         
20359         this.fireEvent("show", this);
20360         
20361         
20362     },
20363     
20364     hide : function()
20365     {
20366         this.fireEvent("beforehide", this);
20367         
20368         this.hidden = true;
20369         this.el.removeClass('open');
20370         
20371         Roo.get(document).un("mouseup", this.onMouseUp);
20372         
20373         this.fireEvent("hide", this);
20374     },
20375     
20376     onMouseUp : function()
20377     {
20378         this.hide();
20379     }
20380     
20381 });
20382
20383  
20384  /*
20385  * - LGPL
20386  *
20387  * menu item
20388  * 
20389  */
20390 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20391
20392 /**
20393  * @class Roo.bootstrap.menu.Item
20394  * @extends Roo.bootstrap.Component
20395  * Bootstrap MenuItem class
20396  * @cfg {Boolean} submenu (true | false) default false
20397  * @cfg {String} html text of the item
20398  * @cfg {String} href the link
20399  * @cfg {Boolean} disable (true | false) default false
20400  * @cfg {Boolean} preventDefault (true | false) default true
20401  * @cfg {String} icon Font awesome icon
20402  * @cfg {String} pos Submenu align to (left | right) default right 
20403  * 
20404  * 
20405  * @constructor
20406  * Create a new Item
20407  * @param {Object} config The config object
20408  */
20409
20410
20411 Roo.bootstrap.menu.Item = function(config){
20412     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20413     this.addEvents({
20414         /**
20415          * @event mouseover
20416          * Fires when the mouse is hovering over this menu
20417          * @param {Roo.bootstrap.menu.Item} this
20418          * @param {Roo.EventObject} e
20419          */
20420         mouseover : true,
20421         /**
20422          * @event mouseout
20423          * Fires when the mouse exits this menu
20424          * @param {Roo.bootstrap.menu.Item} this
20425          * @param {Roo.EventObject} e
20426          */
20427         mouseout : true,
20428         // raw events
20429         /**
20430          * @event click
20431          * The raw click event for the entire grid.
20432          * @param {Roo.EventObject} e
20433          */
20434         click : true
20435     });
20436 };
20437
20438 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20439     
20440     submenu : false,
20441     href : '',
20442     html : '',
20443     preventDefault: true,
20444     disable : false,
20445     icon : false,
20446     pos : 'right',
20447     
20448     getAutoCreate : function()
20449     {
20450         var text = [
20451             {
20452                 tag : 'span',
20453                 cls : 'roo-menu-item-text',
20454                 html : this.html
20455             }
20456         ];
20457         
20458         if(this.icon){
20459             text.unshift({
20460                 tag : 'i',
20461                 cls : 'fa ' + this.icon
20462             })
20463         }
20464         
20465         var cfg = {
20466             tag : 'li',
20467             cn : [
20468                 {
20469                     tag : 'a',
20470                     href : this.href || '#',
20471                     cn : text
20472                 }
20473             ]
20474         };
20475         
20476         if(this.disable){
20477             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20478         }
20479         
20480         if(this.submenu){
20481             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20482             
20483             if(this.pos == 'left'){
20484                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20485             }
20486         }
20487         
20488         return cfg;
20489     },
20490     
20491     initEvents : function() 
20492     {
20493         this.el.on('mouseover', this.onMouseOver, this);
20494         this.el.on('mouseout', this.onMouseOut, this);
20495         
20496         this.el.select('a', true).first().on('click', this.onClick, this);
20497         
20498     },
20499     
20500     onClick : function(e)
20501     {
20502         if(this.preventDefault){
20503             e.preventDefault();
20504         }
20505         
20506         this.fireEvent("click", this, e);
20507     },
20508     
20509     onMouseOver : function(e)
20510     {
20511         if(this.submenu && this.pos == 'left'){
20512             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20513         }
20514         
20515         this.fireEvent("mouseover", this, e);
20516     },
20517     
20518     onMouseOut : function(e)
20519     {
20520         this.fireEvent("mouseout", this, e);
20521     }
20522 });
20523
20524  
20525
20526  /*
20527  * - LGPL
20528  *
20529  * menu separator
20530  * 
20531  */
20532 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20533
20534 /**
20535  * @class Roo.bootstrap.menu.Separator
20536  * @extends Roo.bootstrap.Component
20537  * Bootstrap Separator class
20538  * 
20539  * @constructor
20540  * Create a new Separator
20541  * @param {Object} config The config object
20542  */
20543
20544
20545 Roo.bootstrap.menu.Separator = function(config){
20546     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20547 };
20548
20549 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20550     
20551     getAutoCreate : function(){
20552         var cfg = {
20553             tag : 'li',
20554             cls: 'divider'
20555         };
20556         
20557         return cfg;
20558     }
20559    
20560 });
20561
20562  
20563
20564